【题目来源】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 }