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