標籤:
這道題與皇后問題極像,只是兩者的攻擊範圍不一樣,同時根據題目限制可以發現,這道題資料的特殊性,棋盤很小,因此想到用狀態壓縮DP的方法求解。
首先將每一行互不侵犯的可能列出來,用1、0的方式記錄,之後根據要求會發現,每一行的情況受上一行的情況限制,於是從第一行進行一層層的判斷。又由於國王的攻擊是一個九宮格,因此痛點在於兩國王處於對角,則進行判斷時將下一行向左移一位或向右移一位再進行判斷。最後記得國王數是一定的,用一個變數記錄一下。
程式無太大的突出,但每一步寫的較清晰,希望對大家有所協助。
#include<cstdlib>#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#define MAXN 100 //對MAXN進行賦值 。 using namespace std;long long int f[MAXN]/*行數*/[MAXN]/*狀態*/[600]/*當前國王總數*/,i_total/*最終結果*/; int stay[MAXN],cnt[MAXN]; // stay用於記錄每種狀態壓縮後的值。 cnt用於記錄對應的狀態中的1的個數。int map[MAXN]/*i*/[MAXN]/*j*/; // 當上一狀態為 i,下一狀態為 j時,是否合法。 int i_side,i_number,i_temp=0; //邊長、數量、每種狀態的個數。 void pre_dfs(int x,int y,int z) //預先處理:x 是放了幾顆國王,y 是當前放的位置,z 是目前狀態壓縮後的值。 { stay[++i_temp]=z; //在stay數組中進行儲存,記錄目前的狀壓值。 cnt[i_temp]=x; // 在cnt數組中進行儲存,記錄已放國王的數目 if(x>=(i_side+1)/2||x>=i_number) //如果x超範圍限制,則返回停止。 { return; } for(int i=y+2;i<=i_side;i++) //用for迴圈重複所有可能,將每一種可能用遞迴進行記錄。 { pre_dfs(x+1,i,z+(1<<(i-1))); } }void pre_map(){ for(int i=1;i<=i_temp;i++) { for(int j=1;j<=i_temp;j++) { map[i][j]=map[j][i]=((stay[i]&stay[j])||((stay[i]>>1)&stay[j])||((stay[i]<<1)&stay[j]))?0:1;//對 map[i][j]、 map[j][i]進行賦值,如果不移動衝突或左移衝突或右移衝突,則賦值為0,反之為1。 } } for(int i=1;i<=i_temp;i++) { f[1][cnt[i]][i]=1; //用f數組記錄處於第一行、cnt[i]的狀態下、有 i個國王時的情況。 } }int main(){ scanf("%d%d",&i_side,&i_number);//輸入邊長及王的個數 pre_dfs(0,-1,0); //傳入初始值進行深搜。 pre_map(); // 預先處理一下每種狀態。 for(int i=2;i<=i_side;i++) { for(int j=0;j<=i_number;j++) //國王必須能全部放下。 { for(int q=1;q<=i_temp;q++) //枚舉上一行狀態。 { if(cnt[q]>j)//狀態國王數量要小於等於前 i行國王數量 j。 { continue; } for(int i_number=1;i_number<=i_temp;i_number++ { if(map[i_number][q]&&cnt[i_number]+cnt[q]<=j) { f[i][j][q]+=f[i-1][j-cnt[q]][i_number]; } } } } } for(int i=1;i<=i_temp;i++) //計算最終值。 { i_total=i_total+f[i_side][i_number][i]; } printf("%lld\n",i_total); //輸出結果。 return 0;}
BZOJ 1087 互不侵犯king