gougou1039

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

1039
Family

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1509

15 2006-08-19 06:49:43 00:07.79 27716K C++ glimpse

   拓扑排序+高精度。

   人是种奇怪的小动物。作为一头ws熊,我也这样。没有AC时把这题骂得狗血喷头,AC了就开始教育ZZT,说这是道经典题。或许看别人做题小郁闷时,心里会窃喜,嘎嘎。(大家不要学我哦)

   不过AC这道题的旅程真的有点坎坷。我大约在7月29日AC了其余39题,离圆满只有一题之遥。尝试几次之后,终于在20天后鼓足勇气,用了将近8个小时AC掉了它。

   题目的描述比较的清楚,相信大家看了一遍都看懂了。OK。有多少人看明白81.5%是怎么算出来的?那就再看一遍题,看看题目里有没有定义?……不用找了,是没有的。全靠你YY。

我自己有始至终都没有YY出。ZZT帮我YY的,两个怪物a,b之间的关系定义如下:

1.如果a == b,那么relation = 100%

2.如果a,b都是天生Monster,那么他们之间的relation = 0

3.relation = (a 和 b 父亲的relation + a 和 b 母亲的relation)/2

我们看一下sample:

1,2都是野生Monster,毫无疑问是0

2和6母亲1的relation的关系为0,2和6的父亲2的relation的关系为100,所以2和6的关系为50%

7和5的关系:5和7的父亲5的relation为100,5和7母亲6的relation为62.5(5和4为25,5和5为 100),所以7和5的relation为81.5%

3和3的relation为100%

   弄明白两个怪物之间的血缘计算方法,这个题目就算解决了一半。因为询问的次数比较多并且方便coding,我决定将任意两个怪物之间的relation 都计算出来。

   直接写二重循环肯定是不行的。因为还没有理清怪物之间的父子关系,很有可能在计算a,b的时候,a和b父亲的关系还没有计算。

我的方法是先拓扑一下怪物的家谱,然后按照家谱计算怪物之间的关系。设怪物的拓扑顺序在数组ret中
{{{
  1    
  2    for( int i = 0 ; i < n; i++ )
  3        for( int j = 0; j<=i; j++ )
  4           a[ret[i]][ret[j]]=a[ret[j]][ret[i]]=cal(i,j);
  5    
}}}
   这样可以保证在计算ret[i]和ret[j]之前,ret[i]和ret[j]父母(如果有)的关系都计算过。

   然后根据查询,直接out结果就可以了。

   如果题目能够让我们只保留6位小数,到这里就可以AC了。然而变态的题目要求输出一个准确的值。

   Double肯定是不行的,也没有大浮点数的类,怎么办呢?伟大的狗狗提供了分数类,只要将分子分母从int改成那个300行的大数,理论上就可以得到正确的解。事实上我做了尝试,也证明这个方法是可以的,但Judge无情的把我TLE了。看来取巧的方法是不行的。

   老老实实静下心来,我们不难发现,两个Monster之间的relation可以用一个二进制数表示。第i位为1,就表示relation = relation + (1 / 2^i)。抱歉这段话的内容在做题时是种自发的感觉,所以不知道怎么表达,仔细想想:)

   计算两个怪物之间的关系就是把两个relation都往左(右?反正就是让i+1)一位,再加起来。(注意进位哦)

Out的时候数组得到的就是这个01串。如何将这个01串编成最后的浮点数呢?我们不妨找找规律:
{{{
i:0  100%   1           1
i:1   50%   0.5         05
i:2   25%   0.25        025
i: 3 12.5%  0.125       0125
i: 4  0.625  0.0625     00625
……
}}}
   这么都就足够了。在数值上 n [ i + 1 ] = n [ i ] * 5。如果 n [ i + 1 ]的第一位比n [ i ] 大,那么i+1其实比i要离小数点远了一位。

   初始化的时候写个大数*int(5)的函数,把所有的i位对应的5^i都保存起来。这样没次out的时候,相当于就是做一遍高精度的加法。

   问题到这里似乎得到了较为圆满的解决。然而我WA了。WA的我莫名其妙,怀疑测试数据(当一个人接近崩溃时得正常反应),到POJ上交还是WA。

   仔细又读了一遍题,发现Monster之间的关系最多可以相差300代(或者是299,298之类的),我的数组越界了,我没有用足够的位数保存 relation的长度,也没有计算足够多的5^i次方。将数组调大,POJ AC。

   兴冲冲回到ZOJ上,my god MLE。分析一下,relation数组太大,int a[300][300][300],确实要爆,改成short,仍然爆。无奈只好改成bool型,学着逻辑电路写了个加法器。

   终于AC。

----

会java的有福啦,用BigDecimal轻松AC哦。

注意求概率一定要按顺序,简单的记忆化搜索可能是错的,因为calc(parent1[a], a) = calc(parent1[a], parent1[a]) + calc(parent1[a], parent2[a])是对的,但calc(parent1[a], a) = calc(parent1[parent1[a]], a) + calc(parent2[parent1[a]], a)可是错的哦

1039

Family

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1509

15 2006-08-19 06:49:43 00:07.79 27716K C++ glimpse

拓扑排序+高精度。

人是种奇怪的小动物。作为一头ws熊,我也这样。没有AC时把这题骂得狗血喷头,AC了就开始教育ZZT,说这是道经典题。或许看别人做题小郁闷时,心里会窃喜,嘎嘎。(大家不要学我哦)

不过AC这道题的旅程真的有点坎坷。我大约在7月29日AC了其余39题,离圆满只有一题之遥。尝试几次之后,终于在20天后鼓足勇气,用了将近8个小时AC掉了它。

题目的描述比较的清楚,相信大家看了一遍都看懂了。OK。有多少人看明白81.5%是怎么算出来的?那就再看一遍题,看看题目里有没有定义?……不用找了,是没有的。全靠你YY。

我自己有始至终都没有YY出。ZZT帮我YY的,两个怪物a,b之间的关系定义如下:

1.如果a == b,那么relation = 100%

2.如果a,b都是天生Monster,那么他们之间的relation = 0

3.relation = (a 和 b 父亲的relation + a 和 b 母亲的relation)/2

我们看一下sample:

1,2都是野生Monster,毫无疑问是0

2和6母亲1的relation的关系为0,2和6的父亲2的relation的关系为100,所以2和6的关系为50%

7和5的关系:5和7的父亲5的relation为100,5和7母亲6的relation为62.5(5和4为25,5和5为 100),所以7和5的relation为81.5%

3和3的relation为100%

弄明白两个怪物之间的血缘计算方法,这个题目就算解决了一半。因为询问的次数比较多并且方便coding,我决定将任意两个怪物之间的relation 都计算出来。

直接写二重循环肯定是不行的。因为还没有理清怪物之间的父子关系,很有可能在计算a,b的时候,a和b父亲的关系还没有计算。

我的方法是先拓扑一下怪物的家谱,然后按照家谱计算怪物之间的关系。设怪物的拓扑顺序在数组ret中

  1    
  2    for( int i = 0 ; i < n; i++ )
  3        for( int j = 0; j<=i; j++ )
  4           a[ret[i]][ret[j]]=a[ret[j]][ret[i]]=cal(i,j);
  5    

这样可以保证在计算ret[i]和ret[j]之前,ret[i]和ret[j]父母(如果有)的关系都计算过。

然后根据查询,直接out结果就可以了。

如果题目能够让我们只保留6位小数,到这里就可以AC了。然而变态的题目要求输出一个准确的值。

Double肯定是不行的,也没有大浮点数的类,怎么办呢?伟大的狗狗提供了分数类,只要将分子分母从int改成那个300行的大数,理论上就可以得到正确的解。事实上我做了尝试,也证明这个方法是可以的,但Judge无情的把我TLE了。看来取巧的方法是不行的。

老老实实静下心来,我们不难发现,两个Monster之间的relation可以用一个二进制数表示。第i位为1,就表示relation = relation + (1 / 2^i)。抱歉这段话的内容在做题时是种自发的感觉,所以不知道怎么表达,仔细想想:)

计算两个怪物之间的关系就是把两个relation都往左(右?反正就是让i+1)一位,再加起来。(注意进位哦)

Out的时候数组得到的就是这个01串。如何将这个01串编成最后的浮点数呢?我们不妨找找规律:

i:0  100%   1           1
i:1   50%   0.5         05
i:2   25%   0.25        025
i: 3 12.5%  0.125       0125
i: 4  0.625  0.0625     00625
……

这么都就足够了。在数值上 n [ i + 1 ] = n [ i ] * 5。如果 n [ i + 1 ]的第一位比n [ i ] 大,那么i+1其实比i要离小数点远了一位。

初始化的时候写个大数*int(5)的函数,把所有的i位对应的5^i都保存起来。这样没次out的时候,相当于就是做一遍高精度的加法。

问题到这里似乎得到了较为圆满的解决。然而我WA了。WA的我莫名其妙,怀疑测试数据(当一个人接近崩溃时得正常反应),到POJ上交还是WA。

仔细又读了一遍题,发现Monster之间的关系最多可以相差300代(或者是299,298之类的),我的数组越界了,我没有用足够的位数保存 relation的长度,也没有计算足够多的5^i次方。将数组调大,POJ AC。

兴冲冲回到ZOJ上,my god MLE。分析一下,relation数组太大,int a[300][300][300],确实要爆,改成short,仍然爆。无奈只好改成bool型,学着逻辑电路写了个加法器。

终于AC。


会java的有福啦,用BigDecimal轻松AC哦。

注意求概率一定要按顺序,简单的记忆化搜索可能是错的,因为calc(parent1[a], a) = calc(parent1[a], parent1[a]) + calc(parent1[a], parent2[a])是对的,但calc(parent1[a], a) = calc(parent1[parent1[a]], a) + calc(parent2[parent1[a]], a)可是错的哦