狀態壓縮入門 POJ
阿新 • • 發佈:2019-02-14
#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; } void init(){ //遍歷所有可能的狀態 top = 0; int total = 1 << N; //遍歷狀態的上界 for(int i = 0; i < total; ++i){ if(ok(i))state[++top] = i; } } inline bool fit(int x,int k){ //判斷狀態x 與第k行的實際狀態的逆是否有‘重合’ if(x&cur[k])return false; //若有重合,(即x不符合要求) return true; //若沒有,則可行 } int main(){ while(scanf("%d%d",&M,&N)!= EOF){ init(); memset(dp,0,sizeof(dp)); for(int i = 1; i <= M; ++i){ cur[i] = 0; int num; for(int j = 1; j <= N; ++j){ //輸入時就要按位來儲存,cur[i]表示的是第i行整行的情況,每次改變該數字的二進位制表示的一位 scanf("%d",&num); //表示第i行第j列的情況(0或1) if(num == 0) //若該格為0 cur[i] +=(1<<(N-j)); //則將該位置為1(注意要以相反方式儲存,即1表示不可放牧 } } 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為符合條件的所有狀態) */
//的確是有3重迴圈 ,第一重是對於每一行, 第二重是對於沒一個與當前行沒有衝突的狀態,第三重是對於與上一行沒有衝突的狀態
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行實際情況 // 判斷在i-1行放置狀態j的話會不會與i-1行的土地衝突 if(state[k]&state[j])continue; //判斷是否與第i行衝突 // 判斷如果在i-1行放的狀態為j的話會不會有衝突 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); } }