CodeForces 268D a nice dp

//先贴一发错误的代码,dp[层数][方向]

//因为是对不完整的方案计数了...

 1 #include "iostream"
 2 #include "cstdio"
 3 #include "cstring"
 4 #include "algorithm"
 5 using namespace std;
 6 const __int64 mod = 1e9 + 9;
 7 __int64 dp[1010][5];
 8 int n, h;
 9 
10 int main()
11 {
12     int i, j, k;
13     scanf("%d%d", &n, &h);
14     for(j = 1; j <= 4; ++j)
15         dp[1][j] = 1;
16     for(i = 2; i <= n; ++i) {
17         for(j = 1; j <= 4; ++j) {
18             for(k = max(1, i - h); k <= i - 1; ++k) {
19                 dp[i][j] += dp[k][j];
20                 dp[i][j] %= mod;
21             }
22             //printf("dp[%d][%d] == %I64d\n", i, j, dp[i][j]);
23         }
24     }
25     __int64 res = 0;
26     for(i = n - h + 1; i <= n; ++i)
27         for(j = 1; j <= 4; ++j)
28             res += dp[i][j], res %= mod;
29     printf("%I64d\n", res);
30 }

//将方案补充完整又不至于方案之间的重复,直观的方法构造到 [n - h + 1, n] 中的任意一层的上层都是不可达的,但是 dp[i][j] 是过去所有状态的最好解释,用一种优美的方式分析出 dp[i][j] 且又达到构造不可达层的目的......还是换个姿势吧

 

//看了一下网上的题解,看到了别人构造的状态简直可怕...被吓到的我决定自己模仿构造一个压压惊,结果写出了 __int64 dp[1010][31][31][31][31]; 这个傻逼的超大状态...没办法去压缩一下空间吧,结果在第五组数据上 T 了... 跟正解相比,时间和空间消耗都增加了...

//Time limit exceeded on test 5

 1 #include "iostream"
 2 #include "cstdio"
 3 #include "cstring"
 4 #include "algorithm"
 5 using namespace std;
 6 const int mod = 1e9 + 9;
 7 int dp[2][31][31][31][31];
 8 int n, h;
 9 void add(int &ans, int val)
10 {
11     if((ans += val) > mod)
12         ans -= mod;
13 }
14 
15 int main()
16 {
17     int i, a, b, c, d;
18     int val;
19     bool now = 0;
20     scanf("%d%d", &n, &h);
21     dp[0][0][0][0][0] = 1;
22     for(i = 0; i <= n - 1; ++i) {
23         now ^= 1;
24         memset(dp[now], 0, sizeof(dp[now]));
25         for(a = 0; a <= h; ++a) {
26             for(b = 0; b <= h; ++b) {
27                 for(c = 0; c <= h; ++c) {
28                     for(d = 0; d <= h; ++d) {
29                         if(val = dp[now^1][a][b][c][d]) {
30                             add(dp[now][a < h? 0: h][b < h? b + 1: h][c < h? c + 1: h][d < h? d + 1: h], val);
31                             add(dp[now][a < h? a + 1: h][b < h? 0: h][c < h? c + 1: h][d < h? d + 1: h], val);
32                             add(dp[now][a < h? a + 1: h][b < h? b + 1: h][c < h? 0: h][d < h? d + 1: h], val);
33                             add(dp[now][a < h? a + 1: h][b < h? b + 1: h][c < h? c + 1: h][d < h? 0: h], val);
34                         }
35                     }
36                 }
37             }
38         }
39     }
40     int res = 0;
41     for(a = 0; a <= h; ++a)
42         for(b = 0; b <= h; ++b)
43             for(c = 0; c <= h; ++c)
44                 for(d = 0; d <= h; ++d)
45                     if((val = dp[now][a][b][c][d]) && (a < h || b < h || c < h || d < h))
46                         add(res, dp[now][a][b][c][d]);
47     printf("%d\n", res);
48 }

 

//按照正解的思路写了一下

 1 #include "iostream"
 2 #include "cstdio"
 3 #include "cstring"
 4 #include "algorithm"
 5 using namespace std;
 6 const int mod = 1e9 + 9;
 7 int dp[1010][2][31][31][31];
 8 int n, h;
 9 void add(int &ans, int val)
10 {
11     if((ans += val) > mod)
12         ans -= mod;
13 }
14 
15 int main()
16 {
17     int i, a, b, c, d;
18     int val;
19     scanf("%d%d", &n, &h);
20     dp[0][0][0][0][0] = 1;
21     for(i = 0; i <= n - 1; ++i) {
22         for(a = 0; a <= 1; ++a) {
23             for(b = 0; b <= h; ++b) {
24                 for(c = 0; c <= h; ++c) {
25                     for(d = 0; d <= h; ++d) {
26                         if(val = dp[i][a][b][c][d]) {
27                             add(dp[i + 1][a < 1? 0: 1][b < h? b + 1: h][c < h? c + 1: h][d < h? d + 1: h], val);
28                             add(dp[i + 1][b < h? 0: 1][c < h? c + 1: h][d < h? d + 1: h][a < 1? a + 1: h], val);
29                             add(dp[i + 1][c < h? 0: 1][d < h? d + 1: h][a < 1? a + 1: h][b < h? b + 1: h], val);
30                             add(dp[i + 1][d < h? 0: 1][a < 1? a + 1: h][b < h? b + 1: h][c < h? c + 1: h], val);
31                         }
32                     }
33                 }
34             }
35         }
36     }
37     int res = 0;
38     for(a = 0; a <= 1; ++a)
39         for(b = 0; b <= h; ++b)
40             for(c = 0; c <= h; ++c)
41                 for(d = 0; d <= h; ++d)
42                     if((val = dp[n][a][b][c][d]) && (a < 1 || b < h || c < h || d < h))
43                         add(res, dp[n][a][b][c][d]);
44     printf("%d\n", res);
45 }

//正解大概的意思是,我们每在一层选取一个方向建造一个 bar 之后,可以影响到包括另外三个方向在内(一共四个方向)的距离状态,然后出题人用智商将一个方向的距离状态压缩成了 [2] ,分别表示 bar 的可达与不可达(具体做法是选取这个方向为建造 bar 的方向,hint:因为我们每次都会在这个方向上建造 bar,所以 如果在刚刚建造的方向建造 新bar 或者 另外三个方向在前一个状态时距离状态还没到达 h - 1,那么在 4 个方向上建造的 新bar 都是可达的),四个方向依次旋转一遍表示在四个方向上建造 bar

posted @ 2015-02-20 20:04  AC_Phoenix  阅读(538)  评论(0编辑  收藏  举报