题目链接:http://poj.org/problem?id=3254
题意:
一个n*m的矩形,告诉你哪些格子可以放棋子,哪些格子不能放(1表示能放,0表示不能放)。且相邻格子不能放。问一共有多少种放棋子的方案。(一个棋子也不放算一种方案,答案对1e8取模)。
题解:
状态压缩递推。
设dpi,s表示第i行,方案为s(s为一个用来记录方案的二进制数,被选列为1,不被选列为0)。则dpi,s = sum{dpi-1,ss||s&ss==0} [s合法] || 0 [s不合法](s合法的判断标准为是否选了左右相邻格子,s&ss==0判断的是是否选了上下相邻格子) 。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define LL long long 5 #define RI register int 6 #define KI 100000000 7 using namespace std; 8 const int INF = 0x7ffffff ; 9 const int N = 12 + 2 ; 10 11 inline int read() { 12 int k = 0 , f = 1 ; char c = getchar() ; 13 for( ; !isdigit(c) ; c = getchar()) 14 if(c == '-') f = -1 ; 15 for( ; isdigit(c) ; c = getchar()) 16 k = k*10 + c-'0' ; 17 return k*f ; 18 } 19 int n, m ; 20 bool v[N][N] ; int dp[N][1<<N] ; 21 22 int main() { 23 n = read(), m = read() ; 24 for(int i=1;i<=n;i++) 25 for(int j=1;j<=m;j++) 26 v[i][j] = read() ; 27 dp[1][0] = 1 ; 28 for(int s=1;s<(1<<m);s++) { 29 bool flag = 0 ; 30 for(int j=1;j<=m;j++) { 31 if(!v[1][j] && s&(1<<(j-1))) { 32 flag = 1; break ; 33 } 34 } 35 for(int j=1;j<m;j++) if(s&(1<<(j-1)) && s&(1<<j)) { flag = 1 ; break ; } 36 if(!flag) dp[1][s] = 1 ; 37 } 38 for(int i=2;i<=n;i++) { 39 for(int s=0;s<(1<<m);s++) { 40 dp[i][s] = 0 ; bool flag = 0 ; 41 for(int j=1;j<=m;j++) 42 if(!v[i][j] && s&(1<<(j-1))) { // 该状态中有不能放的格子 43 flag = 1 ; break ; 44 } 45 for(int j=1;j<m;j++) if(s&(1<<(j-1)) && s&(1<<j)) { 46 flag = 1 ; break ; 47 } 48 if(flag) continue ; 49 for(int ss=0;ss<(1<<m);ss++) { // 枚举上一行状态 50 if(ss&s) continue ; // 上下格相邻 51 dp[i][s] += dp[i-1][ss] ; dp[i][s] %= KI ; 52 } 53 } 54 } 55 int ans = 0 ; 56 // for(int s=0;s<(1<<m);s++) printf("%d %d ",s,dp[1][s]) ; 57 for(int s=0;s<(1<<m);s++) ans = (ans+dp[n][s])%KI ; 58 printf("%d",ans) ; 59 return 0 ; 60 }