人生第一个状压dp,虽然是看了题解艰难完成的,也算是把这题搞懂了,以前虽然稍微了解过状压dp,但是没想到实现起来也这么难。
这题就是给你的图中出现1的位置可以放牛,但是每个牛不能相邻(横竖相邻),一般状压的数据范围会比较小,然后就是用位运算把每行的情况都存起来。
这题首先要把1不会出现相邻的情况全部打表算出来,存储在state数组中(这个情况有377种)。
然后如果state[i]的每一个1的位置,在当前第j行的位置都是1,就是说第j行满足第i种情况,在dp[j][i]中存储这行这种情况的个数,如何来判断state[i]中的每个1在第j行中也是1呢,看了人家的题解发现真是精妙,将第j行的0出现的位置用位运算存起来,如果state[i]&a[j]为正,就是state[i]中的1在第j行的0的位置出现,那么肯定不满足这种情况了,如果运算结果位0,那么就是state[i]的1都出现在第j行的1的位置。
状态转移方程是dp(i,j)=sigma dp(i-1,k)state[j]&state[k]=0。(i表示行,j,k表示state中的第几种情况,然后这两种情况与运算等于0,就是竖直方向不能出现相邻)。
状压dp对于我来说还是很陌生啊,我得再搞明白几个题。
#include<iostream> #include<cstdio> #include<cctype> #include<cstdlib> #include<cmath> #include<algorithm> #include<cstring> #include<string> #include<vector> #include<queue> #include<map> #include<set> #include<sstream> #include<stack> using namespace std; #define MAX 105 typedef long long LL; const double pi=3.141592653589793; const int INF=1e9; const double inf=1e20; const double eps=1e-6; int dp[15][400]; int m,n,top; int state[400];//所有1不相邻的情况先存起来 int a[15];//每行的情况(把0出现的位置存起来,和state中的情况进行位运算,如果非0,就是这行的情况不满足state中的这个情况) bool ok(int x){//计算有多少种1不相邻的状态 if(x&x<<1) return false; return true; } void init(){ int total=1<<n; top=0; for(int i=0;i<total;i++){ if(ok(i)) state[top++]=i; } } bool fit(int x,int y){ if(x&a[y]) return 0;//判断这一行的状态是否包含state中满足条件的状态 return 1; } int main(){ scanf("%d%d",&m,&n); init(); memset(dp,0,sizeof(dp)); for(int i=0;i<m;i++){ for(int j=0;j<n;j++){ int x; scanf("%d",&x); if(x==0) a[i]+=(1<<j);//将0储存,因为需要state中的情况中的1完全包含于这一行的1,才算满足情况,将0储存做&运算比较方便 } } for(int i=0;i<top;i++){ if(fit(state[i],0)) dp[0][i]=1;//第一行的情况 } for(int i=1;i<m;i++){ for(int k=0;k<top;k++){ if(fit(state[k],i)){//第i行满足的情况 for(int j=0;j<top;j++){ if(fit(state[j],i-1)){//上一行满足的情况 if(state[k]&state[j]) continue;//竖直方向不能有相邻的1 else dp[i][k]=(dp[i][k]+dp[i-1][j])%100000000; } } } } } int ans=0; for(int i=0;i<top;i++) ans=(ans+dp[m-1][i])%100000000; printf("%d\n",ans); return 0; }