ZOJ 2284 题解
题目链接:http://acm.zju.edu.cn/show_problem.php?pid=2284
序列题又出现了,题目比较有意思。
意思大概是这样的,给出一个数字n,在1,2,3,4......n的这些数字中找出
组合的个数,要求这些组合的共同点是它的inversion number是k,一个
inversion number的意思就是1<=i<j<=n and Ai>Aj.例如:2,3,1的inversion
number是2,因为2和1,3和1是2个inversion number。
一开始,我的做法是dfs,不过很显然,这样肯定超时,测试了一下,果然
超时。后来想想这个题目貌似可以用dp,还是很简单的dp。可以这样看,m[i]
[j]表示n=i,k=j时候的结果,要求m[i][j],只要知道了m[i-1][...]就可以了,
比如已知m[3][0],m[3][1],m[3][2],m[3][3],m[3][4]......,现在要求m[4]
[k],要做的只有把4插入到前面三个中有可能插的地方,例如1,2,3可以插的地方
是1前面,1,2之间,2,3之间,3后面,因为4是这些数中最大的数,这样每次插入
产生的新的inversion number的数量是可以直接知道的,只要利用这样简单的关
系,就可以得到m[1][0]到m[20][200]所有的情况。
于是代码如下,in C++,时间是0秒,注意,这里貌似要用long long ,不然会wa
的。
代码
1 #include<iostream>
2 #include<cstdio>
3 using namespace std ;
4 long long m[21][201] ;
5
6 void init()
7 {
8 for(int i = 0 ; i <=200 ; i ++)
9 m[1][i] = 0 ;
10 m[1][0] = 1 ;
11 for(int i = 2 ; i <= 20 ; i ++)
12 for(int j = 0 ; j <= 200 ; j ++)
13 {
14 m[i][j] = 0;
15 for(int k = 0 ; k < i && j - k >= 0 ; k ++)
16 m[i][j] += m[i - 1][j - k] ;
17
18 }
19 }
20 int main()
21 {
22 int n , k ;
23
24 init() ;
25 while(1)
26 {
27 scanf("%d%d",&n,&k) ;
28 if(n == 0 && k == 0)
29 break ;
30 printf("%lld\n",m[n][k]) ;
31 }
32 return 0 ;
33 }
2 #include<cstdio>
3 using namespace std ;
4 long long m[21][201] ;
5
6 void init()
7 {
8 for(int i = 0 ; i <=200 ; i ++)
9 m[1][i] = 0 ;
10 m[1][0] = 1 ;
11 for(int i = 2 ; i <= 20 ; i ++)
12 for(int j = 0 ; j <= 200 ; j ++)
13 {
14 m[i][j] = 0;
15 for(int k = 0 ; k < i && j - k >= 0 ; k ++)
16 m[i][j] += m[i - 1][j - k] ;
17
18 }
19 }
20 int main()
21 {
22 int n , k ;
23
24 init() ;
25 while(1)
26 {
27 scanf("%d%d",&n,&k) ;
28 if(n == 0 && k == 0)
29 break ;
30 printf("%lld\n",m[n][k]) ;
31 }
32 return 0 ;
33 }