P1896 [SCOI2005]互不侵犯 狀壓dp
阿新 • • 發佈:2020-09-12
題目:
題目描述
在N×N的棋盤裡面放K個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各一個格子,共8個格子。
注:資料有加強(2018/4/25)
輸入格式
只有一行,包含兩個數N,K ( 1 <=N <=9, 0 <= K <= N * N)
輸出格式
所得的方案數
輸入輸出樣例
輸入 #13 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)) #definemem__(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; }