基础的状压DP,因为是将状态压缩到一个整数中,所以会涉及到很多比较巧妙的位运算。
我们可以先把输入中每行的01压缩成一个整数。
判断一个状态是否有相邻1:
如果 x & (x << 1) 非0,说明有相邻的两个1
判断一个状态能否放在该行:
如果 (a[i] & state) != state,说明是不能放置的。因为a[i]中存在某个0和对应state中的1,与运算之后改变了state的值
判断相邻两行的状态是否有同一列相邻的1:
如果(state & _state)不为零,说明有相邻的1
1 #include <cstdio> 2 #include <cstring> 3 4 const int maxn = 13; 5 const int M = 100000000; 6 int a[maxn], d[maxn][1 << maxn]; 7 int n, m, tot; 8 9 inline bool check_row(int x) { return x & (x << 1) ? false : true; } //判断一行是否有相邻的 10 inline bool check_col(int x1, int x2) { return x1 & x2 ? false : true; } //判断一列是否有相邻的 11 inline bool check_put(int a, int x) { return (x & a) == x ? true : false; } //判断该状态能否放在该行 12 13 int main() 14 { 15 //freopen("in.txt", "r", stdin); 16 17 while(scanf("%d%d", &n, &m) == 2) 18 { 19 memset(d, 0, sizeof(d)); 20 for(int i = 0; i < n; i++) 21 { 22 int t; 23 a[i] = 0; 24 for(int j = 0; j < m; j++) { scanf("%d", &t); a[i] = a[i] * 2 + t; } 25 } 26 tot = (1 << m); 27 for(int i = 0; i < tot; i++) if(check_row(i) && check_put(a[0], i)) d[0][i] = 1; 28 29 for(int i = 1; i < n; i++) 30 {//枚举行标 31 for(int state = 0; state < tot; state++) 32 {//枚举该行的状态 33 if(!check_row(state) || !check_put(a[i], state)) continue; 34 for(int _state = 0; _state < tot; _state++) 35 {//枚举上一行的状态 36 if(state & _state) continue; 37 d[i][state] = (d[i][state] + d[i-1][_state]) % M; 38 } 39 } 40 } 41 42 int ans = 0; 43 for(int i = 0; i < tot; i++) ans = (ans + d[n-1][i]) % M; 44 printf("%d ", ans); 45 } 46 47 return 0; 48 }