BZOJ 1087 [SCOI2005]互不侵犯King 【狀壓dp】
阿新 • • 發佈:2018-07-19
說明 tro 攻擊 continue i++ mes 題解 左右 簡潔
對於這類棋盤類問題,一般都要 狀壓 。
一開始想,如果要記錄全盤狀態,存不下啊!!!後來經 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 (因為兩行狀態不同,所以左右兩邊都要判一下,同一行的話往左移,往右移效果是一樣的)
貼代碼:
BZOJ 1087 [SCOI2005]互不侵犯King
Description
在N×N的棋盤裏面放K個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上
左下右上右下八個方向上附近的各一個格子,共8個格子。
Input
只有一行,包含兩個數N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output
方案數。
Sample Input
3 2Sample Output
16 題解: 題目描述非常簡潔,做起來。。。。 這道題一開始想---搜索,可是會T。或者組合數?發現推不出。。。 好吧,那還是試試狀壓 dp 吧。。。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 longView Codelong 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 }
dp還是欠缺啊!!!要多練啊!!!dp很重要!!!
加油加油加油!!!fighting fighting fighting!!!
BZOJ 1087 [SCOI2005]互不侵犯King 【狀壓dp】