残缺棋盘--状压DP
P1:给你一个n*m的残缺棋盘(部分位置不可放棋子),求棋子两两不能相邻的方案数mod987654321
分析:这里相邻理解为四联通,我们将每一行看作一个状态,用上一行来推下一行的方案数。
代码:
1 #include "bits/stdc++.h" 2 3 #define ll long long 4 #define maxn 10 5 #define maxm 10005 6 using namespace std; 7 8 int n, m, N, tot, s[maxm]; //s[i]表示第i种可行状态 9 ll ans, f[maxn][maxm]; //f[i][j][l]表示第i行状态为第j种摆放的方案数 10 int a[20][20]; 11 12 bool check(int id, int x) { 13 for (int j = m; j >= 1; j--) { 14 int y = x & 1; 15 if (y == 1 && a[id][j] == 0) 16 return 0; 17 x >>= 1; 18 } 19 return 1; 20 } 21 int main() { 22 cin >> n >> m; 23 for (int i = 1; i <= n; i++) 24 for (int j = 1; j <= m; j++) cin >> a[i][j]; 25 26 N = 1 << m; 27 memset(f, 0, sizeof(f)); 28 for (int i = 0; i < N; ++i) { 29 if (i & (i << 1))continue; 30 s[++tot] = i; 31 if (!check(1, s[tot])) continue; 32 f[1][tot] = 1; //第一行需要单独初始化 33 } 34 for (int i = 2; i <= n; ++i) 35 for (int j = 1; j <= tot; ++j) { 36 if (!check(i, s[j])) continue; 37 for (int k = 1; k <= tot; ++k) { 38 if (s[j] & s[k])continue; 39 f[i][j] += f[i - 1][k]; 40 } 41 } 42 for (int i = 1; i <= tot; ++i) ans += f[n][i]; 43 printf("%lld", ans); 44 return 0; 45 }
八联通的做法与之类似,改一下判断相邻条件即可。