ACM/ICPC 之 DP进阶(51Nod-1371(填数字))

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ACM/ICPC 之 DP进阶(51Nod-1371(填数字))相关的知识,希望对你有一定的参考价值。

原题链接:填数字

顺便推荐一下,偶然看到这个OJ,发现社区运营做得很赞,而且交互和编译环境都很赞(可以编译包括Python,Ruby,Js在内的脚本语言,也可以编译新标准的C/C++11,甚至包括Go和C Sharp等),虽然暂时不太火,但估计会逐渐成为国内算法界非常受欢迎的OJ社区。

主页:http://www.51nod.com/index.html

 


 

  

  本题是个题意简单的,思路复杂的DP题,说实话,光是想出这种DP就已经非常不易了,即便写出来也要考虑清楚每一种转移的公式和数值关系。

  

  原题:有n(1-200)行格子,第i(1<=i<=n)行有i个格子,每行格子是左对齐。现在要在每一个格子填入一个非负整数,最后使得每一行每一列的和都不超过2。

     请计算有多少种方案,答案比较大,请输出对100,000,007(1e8+7)取余后的结果。

     下图是n=4的时候格子的摆放。

      技术分享

 

 1 //务必注意理清每次状态转移方程的思路和公式
 2 //本人因为一个地方写多了个+1,结果WA了5发....
 3 //Memory:34900K Time:93Ms
 4 #include<iostream>
 5 using namespace std;
 6 
 7 #define MAX 201
 8 #define MOD 100000007
 9 
10 #define COL_0 (i - j - k - 1)    //和为0的列数
11 /*
12 * dp[i][j][k]
13 * i:表明当前行
14 * j:表明i行完成时有多少列为1
15 * k:表明j行完成时有多少列为2
16 * dp值表明该状态下的情况数
17 * 每次由 i-1行 -> i行 转移同j同k的状态
18 */
19 __int64 dp[MAX][MAX][MAX];
20 
21 int main()
22 {
23     int n;
24     scanf("%d", &n);
25     dp[1][0][0] = dp[1][1][0] = dp[1][0][1] = 1;
26     for (__int64 i = 2; i <= n; i++)
27         for (__int64 j = 0; j <= i; j++)
28             for (__int64 k = 0; k <= i - j; k++)
29             {
30                 //最后一格为0时
31                 //-可+2
32                 if (i - j - k - 1 >= 1)
33                     dp[i][j][k + 1] = (dp[i][j][k + 1] + dp[i - 1][j][k] * COL_0) % MOD;
34                 //-可+1
35                 //--两个1_0-0
36                 if (i - j - k - 1 >= 2)
37                     dp[i][j + 2][k] = (dp[i][j + 2][k] + dp[i - 1][j][k] * (COL_0 * (COL_0 - 1) / 2)) % MOD;
38                 //--两个1_1-0
39                 if (j >= 1 && i - j - k - 1 >= 1)
40                     dp[i][j][k + 1] = (dp[i][j][k + 1] + dp[i - 1][j][k] * COL_0 *j) % MOD;
41                 //--两个1_1-1
42                 if (j >= 2)
43                     dp[i][j - 2][k + 2] = (dp[i][j - 2][k + 2] + dp[i - 1][j][k] * (j*(j - 1) / 2)) % MOD;
44                 //--一个1_1
45                 if (j >= 1)
46                     dp[i][j - 1][k + 1] = (dp[i][j - 1][k + 1] + dp[i - 1][j][k] * j) % MOD;
47                 //--一个1_0
48                 if (i - j - k - 1 >= 1)
49                     dp[i][j + 1][k] = (dp[i][j + 1][k] + dp[i - 1][j][k] * COL_0) % MOD;
50                 //什么都不加
51                 dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k]) % MOD;
52 
53                 //最后一格为1时
54                 //-可+1_0
55                 if (i - j - k - 1 >= 1)
56                     dp[i][j + 2][k] = (dp[i][j + 2][k] + dp[i - 1][j][k] * COL_0) % MOD;
57                 //-可+1_1
58                 if (j >= 1)
59                     dp[i][j][k + 1] = (dp[i][j][k + 1] + dp[i - 1][j][k] * j) % MOD;
60                 //什么都不加
61                 dp[i][j + 1][k] = (dp[i][j + 1][k] + dp[i - 1][j][k]) % MOD;
62 
63                 //最后一格为2时
64                 dp[i][j][k + 1] = (dp[i][j][k + 1] + dp[i - 1][j][k]) % MOD;
65             }
66 
67     __int64 sum = 0;
68     for (int j = 0; j <= n; j++)
69         for (int k = 0; k <= n - j; k++)
70             sum = (sum + dp[n][j][k]) % MOD;
71     printf("%I64d\n", sum);
72 
73     return 0;
74 }

 

以上是关于ACM/ICPC 之 DP进阶(51Nod-1371(填数字))的主要内容,如果未能解决你的问题,请参考以下文章

ACM/ICPC 之 最长公共子序列计数及其回溯算法(51Nod-1006(最长公共子序列))

ACM/ICPC 之 经典动规(POJ1088-滑雪)

POJ 8471 切割回文 dp北大ACM/ICPC竞赛训练

POJ 1194 Zipper dp北大ACM/ICPC竞赛训练

POJ 8464 股票买卖 dp北大ACM/ICPC竞赛训练

2016 ACM/ICPC Asia Regional Shenyang Online 1009/HDU 5900 区间dp