【DP】【芝麻开门】

【题目来源】http://acm.buaa.edu.cn/problem/403/

 

【个人体会】觉得自己弱爆了。。。当时做的时候实在不会,问了各路大神,得到的解法也不尽相同,有的至今仍感觉懵懵懂懂。当时看完题目后唯一的想法就是任意的前I个房间都必须有大于I的房间钥匙(除非是N),之后再无进展。。。也曾经想过让DP(I)表示到达不了I这个房间的方案数,当时分为两种情况考虑,一是钥匙编号全部小于I,二是有某些房间钥匙的编号大于I,但是这些房间又是到达不了的。情况二让我感觉很麻烦,于是就退缩不再继续想下去了。现在回头看来,情况二实则可以再进一步,不仅仅是不能到达I,更精确是必然也不能到达I之前的某个房间。

 

【题目解析】DP(I)表示的是最远可以到达房间I的方案数,DP(I) = I^I - 最远不能到I的方案数,假设最远能到达房间J, 1<=J<I。那么J+1~I的房间可以随便放钥匙,方案数为I^(I-J)。状态转移方程为DP(I) = I^I - SUM{DP(J) * I ^ (I - J)},只有一个边界条件就是F[1]=1。

      PS:要预先处理出I^J取模的数值,不然会超时。

 

【代码如下】

 

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 typedef long long int64;
 6 
 7 const int mod = 20121215, Maxn = 101;
 8 int N;
 9 int64 List[Maxn][Maxn], F[Maxn];
10 
11 int64 Dp(int i)
12 {
13     if (F[i]) return F[i];
14     if (i == 1) return 1;
15     int64 sum = List[i][i];
16     for (int j = 1; j < i; j ++)
17     {
18         sum -= ((Dp(j) % mod) * List[i][i - j]) % mod;
19         if (sum < 0) sum += mod;
20     }
21     F[i] = sum;
22     return sum;
23 }
24 
25 void Prework()
26 {
27     for (int i = 1; i < Maxn; i ++)
28     {
29         List[i][0] = 1;
30         for (int j = 1; j < Maxn; j ++)
31             List[i][j] = ((List[i][j - 1] % mod) * i) % mod;
32     }
33 }
34 
35 int main()
36 {
37     Prework();
38     while (cin >> N) cout << Dp(N) << endl;
39     return 0;
40 }

 

 

 

posted on 2012-12-29 19:15  孤星ぁ紫辰  阅读(194)  评论(1编辑  收藏  举报

导航