1. 程式人生 > >BZOJ 1087 [SCOI2005]互不侵犯King 【狀壓dp】

BZOJ 1087 [SCOI2005]互不侵犯King 【狀壓dp】

說明 tro 攻擊 continue i++ mes 題解 左右 簡潔

BZOJ 1087 [SCOI2005]互不侵犯King

Description

  在N×N的棋盤裏面放K個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上
左下右上右下八個方向上附近的各一個格子,共8個格子。

Input

  只有一行,包含兩個數N,K ( 1 <=N <=9, 0 <= K <= N * N)

Output

  方案數。

Sample Input

3 2

Sample Output

16 題解: 題目描述非常簡潔,做起來。。。。 這道題一開始想---搜索,可是會T。或者組合數?發現推不出。。。 好吧,那還是試試狀壓 dp 吧。。。
對於這類棋盤類問題,一般都要 狀壓 。 一開始想,如果要記錄全盤狀態,存不下啊!!!後來經 dalao 指點,恍然大悟,當前行只與上一行有關系,因為它的範圍只有附近八個格子。。(誒,是我太弱了) 這樣我們設 dp[i][j][k] 為做到第 i 行,共放了 j 個王,第 i 行的狀態為 k。 這樣一來我們就好推了。 dp[i][j][k]= dp[i-1][j-num[i]][last] (num[i] 表示第 i 行放多少個王,last 表示第 i-1 行的狀態) 還剩下一個問題:怎麽判斷合不合法,能不能放。 這裏就要用到強大的位運算技巧了。。。%dalao (狀壓 dp 題,位運算一定要熟練掌握啊
!!!)
判斷同一行相鄰兩個是否合法:只需要 i & (i<<1) ,這樣錯開來,如果為1就說明在某一位置,它的兩邊有王,不合法; 判斷上下兩行的話就要兩次了:既要 i<<1 ,又要 i>>1 (因為兩行狀態不同,所以左右兩邊都要判一下,同一行的話往左移,往右移效果是一樣的) 貼代碼: 技術分享圖片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,k,num[1005];
 4 long long dp[10][1005][1005];
 5 bool flag[1005];
 6 long
long ans; 7 int main() 8 { 9 scanf("%d%d",&n,&k); 10 for (int i=0; i<(1<<n); i++) 11 if (!(i&(i<<1))) 12 { 13 flag[i]=true; 14 int t=i; 15 while (t) 16 { 17 num[i]+=(t&1); 18 t>>=1; 19 } 20 dp[1][num[i]][i]=1; 21 } 22 for (int i=2; i<=n; i++) 23 for (int j=0; j<=k; j++) 24 for (int now=0; now<(1<<n); now++) 25 { 26 if (!flag[now]) continue; 27 if (num[now]>j) continue; 28 for (int last=0; last<(1<<n); last++) 29 { 30 if (!flag[last]) continue; 31 if ((last & now) || ((now<<1)&last) || ((now>>1)&last)) continue; 32 dp[i][j][now]+=dp[i-1][j-num[now]][last]; 33 } 34 } 35 for (int i=0; i<(1<<n); i++) 36 ans+=dp[n][k][i]; 37 cout<<ans<<endl; 38 return 0; 39 }
View Code

dp還是欠缺啊!!!要多練啊!!!dp很重要!!!

加油加油加油!!!fighting fighting fighting!!!

BZOJ 1087 [SCOI2005]互不侵犯King 【狀壓dp】