看了一下午才看懂的状压dp入门题 poj 3254 状态压缩dp

题目大意:农夫有一块地,被划分为m行n列大小相等的格子,其中一些格子是可以放牧的(用1标记),农夫可以在这些格子里放牛,其他格子则不能放牛(用0标记),并且要求不可以使相邻格子都有牛。现在输入数据给出这块地的大小及可否放牧的情况,求该农夫有多少种放牧方案可以选择(注意:任何格子都不放也是一种选择,不要忘记考虑! 

补两张图:来自大佬博客:https://blog.csdn.net/he012/article/details/38382163

代表二进制思想,其中二进制形式的数都是表示的合法的数,即符合题目要求的(在每一行中两个1不相邻)

//思想全部在题解里:
//dp[i][j]:对于前i行数据,每行有前j种可能状态时的解
#include <cstdio>
#include <cstring>
using namespace std;
/*
    放置奶牛,可放时且相邻不可以有的方法数目有多少;
*/
#define mod 100000000
int M,N,top = 0;
//top表示每行最多的状态数
 
int state[600],num[110];  
//state存放每行所有的可行状态(即没有相邻的状态)
//
 
int dp[20][600];
//dp[i][j]:对于前i行数据,每行有前j种可能状态时的解
int cur[20];
//cur[i]表示的是第i行整行的情况
 
inline bool ok(int x){	//判断状态x是否可行
   if(x&x<<1)	return false;//若存在相邻两个格子都为1,则该状态不可行
   return true;//0,1,2,4,5例如这种;分解成二进制不存在相邻的1;
   //其中(x&x<<1)为真时代表相邻同为1,要理解位运算知识;
}
void init(){			//遍历所有可能的状态
   top = 0;
   int total = 1 << N; //遍历状态的上界2^N;记录为所有状态;
   for(int i = 0; i < total; ++i){
       if(ok(i))state[++top] = i;//表示可行?;//可行的数字存储进去;//top在这个地方求出;代表可能的最大状态数目;
   }
}
inline bool fit(int x,int k){ //判断状态x 与第k行的实际状态的逆是否有‘重合’??
   if(x&cur[k])return false; //若有重合,(即x不符合要求)//注意!!!@!
   return true;  //若没有,则可行
}
 //以dp[i][state(j)]来表示对于前i行,第i行采用第j种状态时可以得到的可行方案总数!
int main(){
    while(scanf("%d%d",&M,&N)!= EOF){
       init();
       memset(dp,0,sizeof(dp));
       for(int i = 1; i <= M;++i){//M行;
           cur[i] = 0;//第i行整行的情况;
           int num;
           for(int j = 1; j <= N; ++j){  //输入时就要按位来存储,cur[i]表示的是第i行整行的情况,每次改变该数字的二进制表示的一位
                scanf("%d",&num); //表示第i行第j列的情况(0或1)
               if(num == 0) //若该格为0	//i行j列;
				   cur[i] +=(1<<(N-j));//则将该位置为1(注意要以相反方式存储,即1表示不可放牧z^(N-j)
           }//这个地方还是有一些疑问;
       }
       for(int i = 1;i <= top;i++){
           if(fit(state[i],1)){  //判断所有可能状态与第一行的实际状态的逆是否有重合
                dp[1][i] = 1; //若第1行的状态与第i种可行状态吻合,则dp[1][i]记为1
           }//;
       //初始化;
	   }
	   /*
	   状态转移过程中,dp[i][k] =Sigma dp[i-1][j] (j为符合条件的所有状态)
        */
        //以dp[i][state(j)]来表示对于前i行,第i行采用第j种状态时可以得到的可行方案总数!
       for(int i = 2; i <= M; ++i){  //i索引第2行到第M行
           for(int k = 1; k <= top; ++k){ //该循环针对所有可能的状态,找出一组与第i行相符的state[k]
                if(!fit(state[k],i))continue; //判断是否符合第i行实际情况
                for(int j = 1; j <= top ;++j){ //找到state[k]后,再找一组与第i-1行符合,且与第i行(state[])不冲突的状态state[j]
                   if(!fit(state[j],i-1))continue;  //判断是否符合第i-1行实际情况
                   if(state[k]&state[j])continue;  //判断是否与第i行冲突
                   dp[i][k] = (dp[i][k]+dp[i-1][j])%mod;  //若以上皆可通过,则将'j'累加到‘k'上
                }
           }
       }
       int ans = 0;
       for(int i = 1; i <= top; ++i){ //累加最后一行所有可能状态的值,即得最终结果!!!泥马写注释累死我了终于写完了!
           ans = (ans + dp[M][i])%mod;
       }
       printf("%d\n",ans);
   }
}

猜你喜欢

转载自blog.csdn.net/lanshan1111/article/details/82901724