目錄題目
分析變數定義
剔除左右相鄰的狀態
計算king[ ] 、state[ ]
如何動態規劃?
如何表示限制條件
原博主的**
寫在最後
在n×n的棋盤裡面放k個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。
輸入格式:
只有一行,包含兩個數n,k ( 1 <=n <=9, 0 <= k <= n * n)
輸出格式:
所得的方案數
參考部落格
對原博主的**進行了自己的理解,並改變了敘述方式和順序。
狀態壓縮的主要思想是,用二進位制來表示當前的狀態,然後用二進位制對應的十進位制數來進行儲存。
比如某一行的國王排列是(有,無,有,無,無,有)。我們用1來代表有,0代表無。那麼對應的二進位制為101001,
我們的king[ j ] 陣列表示的是 狀態j 下的國王數。所以 king[41]=3. 狀態41有3個國王。
同時,state[ i ]陣列,就是第 i 個狀態的 國王排列所對應的狀態(十進位制)。 如果這個排列是第乙個,那麼 ans[1]=41;
ans是狀態(剔除左右相鄰)的總數。
我們需要對這些狀態,左右相鄰的狀態進行剔除。
if( ! ((i<<1)&i ) )//不相鄰
有n*n的棋盤,對於每一行,最多有000...000 --- 111...111這麼多種可能的狀態。對應的十進位制是0到
int tot=(1<>=1;//右移一位}}
}
當前行的狀態,跟上一行的狀態(國王的擺放位置,前面所有行已經用了多少個國王)有關。
(剔除 左右相鄰的狀態 後的總數)。這樣,dp時,才能考慮上下行的關係。
初始條件是第一行狀態。
long long dp[10][15000][80]; //dp[i][j][k]表示第i行,狀態為j,前面擺了k個國王時,方案數;
long long king[7777777]; //king[j]表示,在狀態為j時,使用的國王數量
for(int i=1;i<=ans;i++)// 對於第一行
}
我們仍舊利用,動態規劃中,j表示本行狀態,p表示前一行狀態。(左右相鄰狀態初始化中,已經剔除)
(1)如何表示上下相鄰
if(state[j]&state[p]) continue;//上下相鄰
一旦上下相鄰,就必然會出現1&1的情況,結果非0。
(2)如何表示 左下,右上
if(state[j]&(state[p]<<1)) continue;//本行的右上
(3)如何表示 左上,右下
if((state[j]<<1)&state[p]) continue;//本行的左上角
#include#include#include#include#include#include#include#include#includeusing namespace std;
#define ll long long
ll dp[10][15000][80];
ll state[777777],king[7777777];
ll ans,sum;
ll n,k;
inline void init()}}
}int main()
}for(int i=1;i<=n;i++)//不確定在哪一行用光了國王
for(int j=1;j<=ans;j++)//不確定在那種狀態下
sum+=dp[i][j][k];
cout<
有數量限制的dp,要在dp【】中開一位,表示數量。
資料量較小的時候,要考慮到狀壓dp。
洛谷P1896 互不侵犯 狀壓dp
在n n n nn n的棋盤裡面放k kk個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8 88個格仔。n 9 k n n n 9,k n n n 9 k n n 首先暴搜肯定被否定,時間複雜度是指數級的,所以我們就可用上狀態壓...
互不侵犯(洛谷P1896)
題目 在n n的棋盤裡面放k個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。輸入輸出 輸入n,k,輸出有幾種放置方法。n 9,k n 2 樣例輸入輸出 入 3 2 出 16 這道題看範圍就顯然是狀壓dp了吧。其實這道題和...
1896 SCOI2005 互不侵犯 狀壓dp
傳送門 這是一道狀壓dp的經典例題 題目讓輸出所有可能的方案數 很顯然 這是一道動態規劃了 由於國王放置的位置有一定的限制 所以我們要在狀態轉移的過程中增加一維來儲存狀態 我們這一道題假設f i j k 意思是在前i行一共放置了j個國王 第i行國王放置的狀態是k 儲存的值是方案數 首先 我們可以先預...