AC解 - 用动态规划解决一道排列组合计数问题(序关系计算)
原题如下:http://acm.nankai.edu.cn/problem.php?problem=1134
[同时请参考网友pandm发的帖子:http://topic.csdn.net/u/20110525/01/adf4d0b0-2b8e- 4c0a-b8da-07b27f1711cc.html?seed=589816236&r=73524109#r_73524109]
There are 13 possible orderings for three numbers, if we sort them with the relation '<' and '=':
A = B = C , A = B < C , A < B = C ,A < B < C
A < C < B , A = C < B , B < A = C ,B < A < C
B < C < A , B = C < A , C < A = B ,C < A < B
C < B < A
Given an integer n, your task is to find the number of possible orderings for any n real numbers.
Input
There are several test cases. Each case contains only one integer n which is between 1 and 50.
Output
Output the number of different orderings for any n real numbers.
Sample Input
3
Sample Output
13
分析:首先理解题意,像A=B<C和B=A<C这样的关系在本题中应该算一种,而不能算成两 种。下面先推导递推公式:
设有三个元素A,B,C,如果用O代表(A,B,C)其中某个元素,那么使用=和<两个符号对 这三个元素进行排列的结果是:
O=O=O
O=O<O
O<O=O
O<O<O
总共四种(下面将这些排列叫做外部排列),显然根据排列原理,对N个元素作这样的排 列,总共能得到2^(N-1)种,因为有N-1个中间位置。
根据题意,如果对这2^(N-1)种排列形式,分别计算出他们的内部排列数(剔除重复) ,累加这些内部排列数得到的结果就是答案。下面将沿着这条思路推出一个递推公式。
显然可以用x=1,y=2可以简单地表示出关系x<y;同样用x=1,y=1可以简单地表示出关系 x=y; 如果要表示x<=y=z这这样的关系,可以设定x=1,y=2,z=2。这样序关系可以转化为 数字的表示。
先考虑N比较小的几种情况(N=2,3,4):
1) N=2:
在N=2时,可以得到的外部排列结果有两种:
O=O
O<O
如果用数字表示,对应为:
1 1
1 2
2) N=3:
在N=3时,可以得到的外部排列结果有2^2种:
O=O=O
O=O<O
O<O=O
O<O<O
如果用数字表示,对应为:
1 1 1
1 1 2
1 2 2
1 2 3
3) N=4:
在N=4时,可以得到的外部排列结果有2^3种:
O=O=O=O
O=O=O<O
O=O<O=O
O=O<O<O
O<O=O=O
O<O=O<O
O<O<O=O
O<O<O<O
如果用数字表示,对应为:
1 1 1 1
1 1 1 2
1 1 2 2
1 1 2 3
1 2 2 2
1 2 2 3
1 2 3 3
1 2 3 4
在N=4时,外部排列(1 1 2 2)对应的内部排列数可以这样来计算:
(1 1 2 2)可以看成是N=3的外部排列(1 1 2)的基础上附加了一个2。如果设(1 1 2)的 内部排列数为p,那么(1 1 2)的内部排列数为
p.(N/m) ...........(公式1)
其中m为数字2在外部排列(1 1 2 2)中的重复数(这里等于2)。这个公式可以这样推出 :
N=3的外部排列(1 1 2)对应的内部排列数为(利用重排公式):
3!/(F.(m-1)!),其中m-1等于(1 1 2)中2的重复次数。F为剩余的数字(这里只有1)的 重复次数阶乘的积。
这样N=4的外部排列(1 1 2 2)对应的内部排列数为:
4!/(F.m!)=3,其中m等于(1 1 2 2)中2的重复次数,即在N=3的重复次数基础上加1.
这个值=(3!*4)/(F.(m-1)!*m)=[3!/(F.(m-1)!)] * [4/m],这里乘号前面就是上面得出 的N=3时内部排列数。
现在再次回到原题来,每次当N=n+1时,都可以看成是N=n的每个外部排列基础上附加一 个数字s和s+1,这里s等于N=n的相应外部排列最右边的数字。比如,当N=3的一个外部 排列为(1 1 2)时,在N=4时,会生成两个新的外部排列,分别为:
(1 1 2 2)
(1 1 2 3)
也就是说N=n+1情形下,它的外部排列数是N=n的外部排列数两倍,而且每个外部排列的 数字序列是递增的(虽然不是严格单调的)。(这也与上面结论相符:即共有2^(N-1)种 外部排列)
到此为止,先总结一下:我们的目的是计算内部排列总数,而N=n+1每个外部排列对应 的内部排列总数可以通过N=n的相应外部排列的内部排列总数得出,即p.(N/m)。
************************
在N=2时,对每个外部排列最右边数字统计一下它们出现的重复次数,并将重复次数按 顺序排序,可以得到下面的结果:
>>>>>>外部排列最右边数字重复次数统计=[1,2]
同样,在N=3时,可以得到下面的结果:
>>>>>>外部排列最右边数字重复次数统计=[1,1,2,3]
在N=4时,可以得到下面的结果:
>>>>>>外部排列最右边数字重复次数统计=[1,1,1,1,2,2,3,4]
例如N=4时的外部排列为:
1 1 1 1
1 1 1 2
1 1 2 2
1 1 2 3
1 2 2 2
1 2 2 3
1 2 3 3
1 2 3 4
其中第一个外部排列的最后一位为1,它的重复次数为4;第二个外部排列的最后一位为 2,它的重复次数为1;第三个外部排列的最后一位为2,它的重复次数为2.....等等。
根据上面的统计可以发现规律,当重复次数出现m时,在统计结果中出现的频次(即最右 边数字重复次数等于m对应的外部排列的总数)为2^(N-m-1)
例如,N=4,m=1时出现的频次为4=2^(4-1-1)。当m=2时,出现的频次为2=2^(4-2-1)。 这个结论是由外部排序的生成规律造成的。
下面是一个比较重要的结论:
如果N=n的最右边数字重复次数等于m(m>1), 那么它在统计结果出现的频次等于: N=n -1中重复次数等于m-1出现的频次。但是当m=1,在N=n-1中重复次数m-1=0出现频次设为 它全排列的总数(原因见下面举例)。
看一下下面的例子就清楚了:
比如N=4时,重复次数等于m=1次(共4个外部排列),相当于在N=3的所有外部排列右边 添加s+1(设N=3对应的外部排列最右边数字为s)。即(上面结论中在N=3,m=0的情况) :
(1 1 1) + (2) = (1 1 1 2)
(1 1 2) + (3) = (1 1 2 3)
(1 2 2) + (3) = (1 2 2 3)
(1 2 3) + (4) = (1 2 3 4)
重复次数等于m=2次(共2个外部排列),在N=3中只需要找出重复次数等于1(即m-1)的 外部排列,然后重复最右边的数字,就可以构造出N=4中的外部排列。即:
(1 1 2) + (2) = (1 1 2 2)
(1 2 3) + (3) = (1 2 3 3)
重复次数等于m=3次(共1个外部排列),在N=3中只需要找出重复次数等于2(即m-1)的 外部排列,然后重复最右边的数字,就可以构造出N=4中的外部排列。即:
(1 2 2) + (2) = (1 2 2 2)
重复次数等于m=4次(共1个外部排列),在N=3中只需要找出重复次数等于3(即m-1)的 外部排列,然后重复最右边的数字,就可以构造出N=4中的外部排列。即:
(1 1 1) + (1) = (1 1 1 1)
************************
接下来该轮到我们得出递推公式了:
设D(m,i)表示N=i时最右边数字重复次数等于m的所有外部排列对应的内部排列总数(注 意这里终于讨论到内部排列数了!),那么有如下递推公式:
******************(公式2)******************
D(m,i) =
i/m * D(m-1,i-1) (当m != 1, i>1)
[D(1,i-1) + D(2,i-1) + ... + D(i-1,i-1)] * i (当m = 1,i>1)
满足1<=m<=i,初始条件:D(1,1) = 1。
*********************************************
下面讨论如何得出这个公式的:
还是拿简单的情况讨论,当i=4时,
外部排列最右边数字重复次数统计Q=[1,1,1,1,2,2,3,4]
当Q中值m等于2时,对应的i=3中的外部排列为(即重复次数为m-1=1的所有外部排列):
(1 1 2) + (2) = (1 1 2 2) .... (a)
(1 2 3) + (3) = (1 2 3 3) .... (b)
即i=3时对应有两个外部排列。
记其中第一个外部排列(a)对应的内部排列数为P(1 1 2) ,那么根据上面(公式1) , P(1 1 2 2) = P(1 1 2) * i/m (i=4,m=2)。同样,第二个 外部排列的内部排列数:P(1 2 3 3) = P(1 2 3) * i/m (i=4,m=2)。显然,这两个 内部排列计数中因子N/m是共用的,所以:
D(m,4) = P(1 1 2 2) + P(1 2 3 3)
= [P(1 1 2) + P(1 2 3)] * i/m
= D(m-1,3) * i/m
依此类推。。。
但是这里还没有考虑当Q中值m等于1情况呢。
如果i=4,m=1,对应于i=3中的外部排列为(即重复次数为m-1=0的所有外部排列):
(1 1 1) + (2) = (1 1 1 2) .....(d)
(1 1 2) + (3) = (1 1 2 3) .....(e)
(1 2 2) + (3) = (1 2 2 3) .....(f)
(1 2 3) + (4) = (1 2 3 4) .....(g)
即i=4,m=1时在i=3中对应它的所有外部排列。
记其中第一个外部排列(d)对应的内部排 列数为P(1 1 1),那么根据上面(公式1) , P(1 1 1 2) = P(1 1 1) *
i/m (i=4,m=1) 。同样,第二个外部排列的内部排列数:P(1 1 2 3) = P(1 1 2) * i/m (i=4,m=1)
。第三个外部排列的内部排列数:P(1 2 2 3) = P(1 2 2) * i/m (i=4,m=1)。第四 个外部排列的内部排列数:P(1
2 3 4) = P(1 2 3) * i/m (i=4,m=1)。这四个内部 排列计数中因子N/m也是共用的,所以:
D(m,4) = P(1 1 1 2) + P(1 1 2 3) + P(1 2 2 3) + P(1 2 3 4)
= [P(1 1 1) + P(1 1 2) + P(1 2 2) + P(1 2 3)] * i/m
= [D(1,3) + D(2,3) + D(3,3)] * i/m
= [D(1,3) + D(2,3) + D(3,3)] * i (因为m=1)
[注意,这里D(1,3) + D(2,3) + D(3,3)就是i=3的所有内部排列数。]
这样就验证了(公式2)
所以原题的序关系总数,即为所有内部排列的总数,等于:
Count = D(1,N) + D(2,N) + .... + D(N,N) ...........(公式3)
注意到在(公式2)中i/m不要单独计算,如果除不尽, m可以通过D(m-1,i-1)约去。
这样,(公式2)和(公式3)就是我们编程实现中需要用到的公式,显然,用动态规划的做 法需要做两重循环,算法复杂度为O(n^2)。在具体实现中,考虑到N的范围为1-50,计 算结果会超出long integer范围,这里使用了BigInteger。
实现代码如下:
import java.math.BigInteger; import java.util.Scanner; /** * * @author ljs * 2011-05-26 * * There are 13 possible orderings for three numbers, if we sort them with the relation '<' and '=': * A = B = C , A = B < C , A < B = C ,A < B < C * A < C < B , A = C < B , B < A = C ,B < A < C * B < C < A , B = C < A , C < A = B ,C < A < B * C < B < A * * Given an integer n, your task is to find the number of possible orderings for any n real numbers. * */ public class Main { public static BigInteger solve(int n){ BigInteger[] D1 = {new BigInteger("1"),new BigInteger("1")}; for(int i=2;i<=n;i++){ BigInteger[] D2 = new BigInteger[i+1]; //D2[0] is not used D2[1] = new BigInteger("0"); for(int j=1;j<D1.length;j++){ D2[1] = D2[1].add(D1[j]); } D2[1] = D2[1].multiply(new BigInteger(String.valueOf(i))); for(int m=2;m<=i;m++){ BigInteger tmp = D1[m-1].multiply(new BigInteger(String.valueOf(i))); D2[m] = tmp.divide(new BigInteger(String.valueOf(m))); } D1= D2; } BigInteger count = new BigInteger("0"); for(int i=1;i<=n;i++){ count = count.add(D1[i]); } return count; } public static void main(String[] args) { /* long start = System.currentTimeMillis(); int n = 50; for(int i=1;i<=n;i++) { BigInteger count = Main.solve(i); System.out.format("count(n=%d): %d%n",i,count); } long end = System.currentTimeMillis(); double timeElapsed = (end-start)/1000.0; System.out.format("Time elapsed(sec): %.3f%n", timeElapsed); */ Scanner cin=new Scanner(System.in); while(cin.hasNextInt()){ int n = cin.nextInt(); BigInteger count = Main.solve(n); System.out.println(count); } } }
测试结果:
count(n=1): 1
count(n=2): 3
count(n=3): 13
count(n=4): 75
count(n=5): 541
count(n=6): 4683
count(n=7): 47293
count(n=8): 545835
count(n=9): 7087261
count(n=10): 102247563
count(n=11): 1622632573
count(n=12): 28091567595
count(n=13): 526858348381
count(n=14): 10641342970443
count(n=15): 230283190977853
count(n=16): 5315654681981355
count(n=17): 130370767029135901
count(n=18): 3385534663256845323
count(n=19): 92801587319328411133
count(n=20): 2677687796244384203115
count(n=21): 81124824998504073881821
count(n=22): 2574844419803190384544203
count(n=23): 85438451336745709294580413
count(n=24): 2958279121074145472650648875
count(n=25): 106697365438475775825583498141
count(n=26): 4002225759844168492486127539083
count(n=27): 155897763918621623249276226253693
count(n=28): 6297562064950066033518373935334635
count(n=29): 263478385263023690020893329044576861
count(n=30): 11403568794011880483742464196184901963
count(n=31): 510008036574269388430841024075918118973
count(n=32): 23545154085734896649184490637144855476395
count(n=33): 1120959742203056268267494209293006882589981
count(n=34): 54984904077825684862426868390301049750104843
count(n=35): 2776425695289206002630310219593685496163584253
count(n=36): 144199280951655469628360978109406917583513090155
count(n=37): 7697316738562185268347644943000493480404209089501
count(n=38): 421985466101260424678587486718115935844245187819723
count(n=39): 23743057231588741419119534567705900419786127935577533
count(n=40): 1370159636942236704917645663312384364386256449136591915
count(n=41): 81045623051154285047127402304207782853156976521592907421
count(n=42): 4910812975389574954318759599939388855544783946694910718603
count(n=43): 304646637632091740261982544696657582136519552428876887346813
count(n=44): 19338536506753895707368358095646384573117824953447578202397675
count(n=45): 1255482482235481041484313695469155949742941807533901307975355741
count(n=46): 83318804148028351409201335290659562069258599933450396080176273483
count(n=47): 5649570401186486930330812460375430692673276472202704742218853260093
count(n=48): 391229145645351175841837029639030040330277058716846008212321196523435
count(n=49): 27656793065414932606012896651489726461435178241015434306518713649426461
count(n=50): 1995015910118319790635433747742913123711612309013079035980385090523556363
Time elapsed(sec): 0.528