1. 程式人生 > 實用技巧 >P1896 [SCOI2005]互不侵犯 狀壓dp

P1896 [SCOI2005]互不侵犯 狀壓dp

題目:

題目描述

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

注:資料有加強(2018/4/25)

輸入格式

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

輸出格式

所得的方案數

輸入輸出樣例

輸入 #1
3 2
輸出 #1
16

題解:

dp[i][j][k]:第i行狀態為j,且截至到第i行已經放置了k個國王的方案數

這個狀態dp因為(i,j)位置有國王,附近八個位置都不可以放國王,所以狀態互斥多了一點

dp轉移方程:

dp[i][j][r]+=dp[i-1][k][r-num_1[j]];

num_1[j]表示第i個狀態中包含國王的數量

最後把所有dp[n][1--num][k]加起來輸出就可以

1--num是指所有狀態

程式碼:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define mem(a) memset(a,0,sizeof(a))
#define
mem__(a) memset(a,-1,sizeof(a)) typedef long long ll; const int maxn=10; const int N=200; const int INF=0x3f3f3f3f; const double blo=(1.0+sqrt(5.0))/2.0; const double eps=1e-8; ll state[N],dp[maxn][N][105],num_1[N]; int main() { ll n,m,num=0,sum=0; scanf("%lld%lld",&n,&m); for(ll i=0; i<(1
<<n); ++i) { if((i&(i<<1)) || (i&(i>>1))) continue; state[num]=i; ll k=i; //printf("%lld ",i); while(k) { if(k&1) num_1[num]++; k>>=1; } //if(num_1[num]<=m) num++; //else num_1[num]=0; } //printf("\n"); //printf("%lld***\n",num); for(ll i=0; i<num; ++i) { dp[0][i][num_1[i]]=1; //if(num_1[i]==k) sum++; } ll maxx=0; for(ll i=1; i<n; ++i) { for(ll j=0; j<num; ++j) { for(ll k=0; k<num; ++k) { if((state[j]&state[k]) || ((state[j]<<1)&state[k]) || (state[j]&(state[k]<<1))) continue; for(ll r=m;r>=num_1[j];--r) { dp[i][j][r]+=dp[i-1][k][r-num_1[j]]; } } } } for(ll i=0;i<num;++i) { maxx+=dp[n-1][i][m]; } printf("%lld\n",maxx); return 0; }