状压dp就是将一个状态压缩成一个十进制数,再进行状态转移。一般是把一行数据按照合适的进制转化为一个整数。
1、一般来说需要先预处理出来所有可行的状态,判断可行可以用位运算对状态进行操作。
2、然后预处理出第一行的状态。
3、从第二行开始遍历每一行,枚举第i行的所有可行状态,枚举第i-1行的所有可行状态,判断其与本行是否冲突,状态间是否冲突。再进行转移。
对于本题,判断k状态是否与本行冲突,我们可以将本行的逆状态存起来,即0->1,1->0,然后!(state[k]&cur[i])确定k状态与第i行的逆状态没有相交。
题目链接:POJ - 3254
代码:
#include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> using namespace std; #define ll long long #define mod 100000000 int n,m,cur[15],state[1<<13],tot;//cur[i]存第i行的逆状态,state[i]存第i个可行的状态 int dp[15][1<<13]; void init() { tot=0; memset(dp,0,sizeof dp); for(int i=0;i<(1<<m);i++)//m为列数 if(!(i&(i<<1))) state[tot++]=i; } bool fit(int k,int t) { return !(state[k]&cur[t]);//如果k状态与第t行的逆状态有重合,一定不合法 } int main() { while(~scanf("%d%d",&n,&m)) { for(int i=1;i<=n;i++) { int p=0; for(int j=0;j<m;j++) { int d; scanf("%d",&d); p=(p<<1)+!d; } cur[i]=p; } init(); for(int i=0;i<tot;i++) if(fit(i,1)) dp[1][i]=1; for(int i=2;i<=n;i++) { for(int j=0;j<tot;j++)//找出本行的状态 { if(!fit(j,i)) continue; for(int k=0;k<tot;k++)//找出上一行的状态 { if(!fit(k,i-1)) continue;//匹配k状态与原图 if(state[j]&state[k]) continue;//匹配j状态与k状态 dp[i][j]=(dp[i][j]+dp[i-1][k])%mod; } } } int ans=0; for(int i=0;i<tot;i++) ans=(ans+dp[n][i])%mod; printf("%d\n",ans); } return 0; }