ZOJ-3697 Bad-written Number 恶心模拟-状态压缩-动态规划
题意:给定一系列的数码管,这些数码管由3*3的图形构成,相邻的两个数码管紧挨着的一列是重叠的,现在问一共有多少种可能的情况。
解法:将每个数字的可能的显示结果(能亮的地方假设都可以亮)保留起来,然后一个一个数码枚举,一个地方要注意就是相邻意味前面一个数码占用了某一个亮线,那么后面这个数码可以选择是否占用该亮线,而如果前面没用占用该亮线则后面的数码必须占用。使用f[i][j]表示到第i个字符,占用相邻列情况为j时的方案数。
代码如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> #include <iostream> using namespace std; int N, to[9] = { -1, 0, -1, 1, 2, 3, 4, 5, 6 }; char str[3][30000]; char table[128]; // 123, 72, 61, 109, 78, 103, 119, 73, 127, 111 const int MOD = int(1e9)+7; const int MM[4] = {0, 8, 64, 72}; // 分别表示都不取,取上面的一根,取下面一根以及都取 const int RR[4] = {0, 2, 16, 18}; typedef long long LL; LL f[10005][4]; // 其中f[i][j]表示到第i个数字,前面占用了j状态时的方案数 struct NUM { char sta; void show() { for (int i = 0; i < 7; ++i) { printf("%d ", sta & (1 << i) ? 1 : 0); } puts(""); } }n[10005]; void init() { for (int i = 0; i < N; ++i) { int l = i << 1, r = (i+1) << 1; for (int j = 0; j < 3; ++j) { for (int k = l; k <= r; ++k) { int x = j * 3 + k - l; if (str[j][k] != ' ' && str[j][k] != '\0') { n[i].sta |= 1 << to[x]; } } } } } LL solve() { int t, h, flag = 0; memset(f, 0, sizeof (f)); for (int i = 0; i < 4; ++i) { if ((n[0].sta & MM[i]) == MM[i]) { // 表示能够提供这样的一个选取方案 if (table[n[0].sta&(~MM[3-i])]) { f[0][i] = 1; flag = 1; } } } if (!flag) return 0; for (int i = 1; i < N; ++i) { flag = 0; for (int j = 0; j < 4; ++j) { if ((n[i].sta & MM[j]) == MM[j]) { t = n[i].sta & (~MM[3-j]); for (int k = 0; k < 4; ++k) { if (!f[i-1][k]) continue; h = t & (~RR[k]); // 一定要亮的灯 if (table[h]) { f[i][j] += f[i-1][k]; f[i][j] %= MOD; flag = 1; } if (k == 1) { h = t & (~RR[k]) | 1 << 1; if (table[h]) { f[i][j] += f[i-1][k]; f[i][j] %= MOD; flag = 1; } } else if (k ==2) { h = t & (~RR[k]) | 1 << 4; if (table[h]) { f[i][j] += f[i-1][k]; f[i][j] %= MOD; flag = 1; } } else if (k == 3) { h = t & (~RR[k]) | 1 << 1; if (table[h]) { f[i][j] += f[i-1][k]; f[i][j] %= MOD; flag = 1; } h = t & (~RR[k]) | 1 << 4; if (table[h]) { f[i][j] += f[i-1][k]; f[i][j] %= MOD; flag = 1; } h = t & (~RR[k]) | 1 << 1 | 1 << 4; if (table[h]) { f[i][j] += f[i-1][k]; f[i][j] %= MOD; flag = 1; } } } } } if (!flag) return 0; } int sp = ((n[N-1].sta>>3)&1) | ((n[N-1].sta>>5)&2); return f[N-1][sp]; } int main() { table[123] = table[72] = table[61] = table[109] = table[78] = 1; table[103] = table[119] = table[73] = table[127] = table[111] = 1; int T; scanf("%d", &T); getchar(); while (T--) { memset(n, 0, sizeof (n)); scanf("%d", &N); // 表示有N个数字被书写 getchar(); for (int i = 0; i < 3; ++i) { memset(str[i], 0, sizeof (str[i])); gets(str[i]); } init(); printf("%lld\n", solve()); } return 0; }