僅僅是筆者對於狀態壓縮類\(dp\)的初步練習吧。
[題目鏈結](
題目大意:給定乙個\(n*n\)的棋盤,在棋盤上放國王,國王的攻擊範圍是它周圍的八個格仔。求放\(k\)個國王的合法方案數。
本題很像八皇后問題,但是由於搜尋狀態數量太多導致搜尋會超時。我們考慮一下\(dp\)。
先考慮狀態設計:裡面首先要包含當前使用的國王數,還可以包含某一行的資訊。那麼,問題來了,我們怎樣判斷當前狀態是否合法呢?
下面就是今天的重點:狀態壓縮。
先給乙個例子:十進位制下的\(10\)轉為二進位制是\(1010\),我們可以用它表示:
當前行的第乙個格仔沒有國王,第二個有,第三個沒有,第四個有。
那麼我們表示狀態就變得容易了:用乙個十進位制數轉成二進位制數,根據它的某一位來記錄狀態即可。
那麼我們的狀態可以表示為:設\(dp[i][j][k]\)為:第\(i\)行,狀態為\(j\),前\(i\)行一共放了\(k\)個國王的狀態數。
那麼我們如何在轉移的過程中判狀態呢?
考慮到有八個格仔不好判斷,我們可以先把狀態預處理出來。顯然,乙個格仔放了\(king\),那它的左邊和右邊都不能放了。
這個可以乙個\(dfs\)做出來。
那麼我們可以列舉了。先把第一行處理一下,因為不論如何對於任何一種狀態它都有一種情況。
注意我們的\(dp\)是從上往下推的。做到無後效性。
那麼對於之後的每一行,顯然都有我們之前處理出來的那麼多狀態。我們列舉一下。
繼續考慮怎麼判斷上下行是否合法。
若當前行的狀態為\(sat[i]\),列舉到的乙個狀態為\(sat[j]\),則:
若$ sat[i]&sat[j] $,則它們上下有並列的\(1\),就是不合法。
若$ (sat[i]<<1)&sat[j] $,則它們左上和右下有\(1\),就是不合法。
若$ sat[i]&(sat[j]<<1) $,則它們左下和右上有\(1\),就是不合法。
去除掉這些狀態之後,我們可以\(dp\)了:
因為我們求的是放國王數\(k\)個的方案數,我們可以列舉一下當前行對應的狀態所對應的國王數,從\(k\)列舉到\(k-num[i]\),把符合此情況的上一行方案數累加即可。
最後統計答案,注意開\(long\)
\(long\).
\(code:\)
#include#include#includeusing namespace std;
#define int long long
int n,k,cnt,f[10][2000][100];
int sat[2000],sats[2000];
long long ans;
void dfs(int satet,int num,int pos)
dfs(satet,num,pos+1);
dfs(satet+(1<=sats[j];--l)f[i][j][l]+=f[i-1][k][l-sats[j]];
}for(int i=1;i<=cnt;++i)ans+=f[n][i][k];
printf("%lld\n",ans);
return 0;
}
2019 8 31 互不侵犯
在n n的棋盤裡面放k個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。注 資料有加強 2018 4 25 只有一行,包含兩個數n,k 1 n 9,0 k n n 所得的方案數 輸入 1複製 3 2 輸出 1複製 16 題...
互不侵犯(BZOJ1087) 題解
在n n的棋盤裡面放k個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。3 216 本題為scoi2005的題,正解應該是狀態壓縮動態規劃,把所有方案變為二進位制儲存,1為該位置擺放了國王,0為沒有,因為一行最多九個格仔,...
scoi 互不侵犯 king
time limit 10 sec memory limit 162 mb 在n n的棋盤裡面放k個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。只有一行,包含兩個數n,k 1 n 9,0 k n n 方案數。3 216...