1. 程式人生 > >BZOJ-1087 互不侵犯King(狀壓dp)

BZOJ-1087 互不侵犯King(狀壓dp)

題意:給一個n*n的格子,放入k個物體,要求每個物體的相鄰8個方向都不能放物體,問方案數。

題解:dp[i][j][k]表示前i行放了j個物體,且第i行狀態為k的方案數,轉移為dp[i][j][k]=∑dp[i-1][j-num[p]][p],num[p]表示單行狀態為p能放幾個物體,可以先預處理出來,另外判斷相鄰兩行是否符合也有個很簡單的技巧,具體見程式碼。

程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long

using namespace
std; int n, k; ll dp[11][110][(1<<10)+10]; int num[(1<<10)+10]; bool flag[(1<<10)+10]; void init(){ memset(num, 0, sizeof(num)); memset(flag, false, sizeof(flag)); for(int i = 0; i<=(1<<10); i++){ if(!(i&(i<<1))){ flag[i] = true;
int ans = 0; int x = i; while(x){ if(x&1){ ans++; } x>>=1; } num[i] = ans; dp[1][num[i]][i] = 1; } } } int main(){ init(); scanf("%d %d", &n, &k);
for(int i = 2; i<=n; i++){ for(int j = 0; j<=k; j++){ for(int now = 0; now<=(1<<n)-1; now++){ if(flag[now] == false){ continue; } if(num[now]>j){ continue; } for(int p = 0; p<=(1<<n)-1; p++){ if(flag[p] == false){ continue; } if((now&p) || ((now<<1)&p) || ((now>>1)&p)){ continue; } dp[i][j][now]+=dp[i-1][j-num[now]][p]; } } } } ll res = 0; for(int i = 0; i<=(1<<n)-1; i++){ res+=dp[n][k][i]; } cout<<res<<endl; return 0; }