1. 程式人生 > >看了一下午才看懂的狀壓dp入門題 poj 3254 狀態壓縮dp

看了一下午才看懂的狀壓dp入門題 poj 3254 狀態壓縮dp

題目大意:農夫有一塊地,被劃分為m行n列大小相等的格子,其中一些格子是可以放牧的(用1標記),農夫可以在這些格子裡放牛,其他格子則不能放牛(用0標記),並且要求不可以使相鄰格子都有牛。現在輸入資料給出這塊地的大小及可否放牧的情況,求該農夫有多少种放牧方案可以選擇(注意:任何格子都不放也是一種選擇,不要忘記考慮! 

代表二進位制思想,其中二進位制形式的數都是表示的合法的數,即符合題目要求的(在每一行中兩個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);
   }
}