小国王——状压DP
在?n×n 的棋盤上放?k?個國王,國王可攻擊相鄰的?8?個格子,求使它們無法互相攻擊的方案總數。
輸入格式
共一行,包含兩個整數?n?和?k。
輸出格式
共一行,表示方案總數,若不能夠放置則輸出0。
數據范圍
1≤n≤10,
0≤k≤n^2
輸入樣例:
3 2輸出樣例:
16思路:
f[i][j][k]:表示前i行放置了j個國王,且第i行的狀態是k?? ??? ?屬性:cnt
首先考慮當前這一行,找到當前這一行所有的合法狀態(即狀態的縱坐標不相鄰),以及合法狀態的合法轉移狀態(因為當前這一行的狀態肯定是上一行轉移過來的,所以只需要考慮上一行的狀態。 那么上一行什么樣的狀態才能轉移成當前的狀態?由題意發現上一行狀態與當前行狀態的縱坐標不能相鄰,上下不能相鄰,左右也不能相鄰)以便于狀態轉移計算。
狀態表示:
f[i][j][k]:表示前i行放置了j個國王,且第i行的狀態是k?? ??? ?
屬性:cnt
狀態計算:
f[i][j][k] +=? f[i-1][j-cnt[state]][state]
初始狀態:
f[0][0][0]
目標狀態:
f[n+1][k][0]????????
因為只要這層不擺東西,則上一層 只要合法,那一定可以轉移到這一層的這個0狀態
解決代碼:
#include<iostream> #include<string.h> #include<algorithm> #include<cmath> #include<vector> using namespace std;typedef long long LL;const int N = 11, M = 1 << N, C = N * N;int n, m, k; LL f[N][C][M]; //f[i][j][k]:表示前i行放置了j個國王,且第i行的狀態是k 屬性:cnt int cnt[M]; //用來存儲每個合法狀態中有多少個國王 vector<int> legal; //存放合法狀態數組 vector<int> trans[M]; //存放合法狀態的合法轉移狀態 bool check(int state) //檢查縱坐標是否相鄰 {return !(state&state>>1); }int count(int state) {int cnt = 0;for(int i=0;i<n;i++) if(state>>i&1) cnt++;return cnt; }int main() {cin>>n>>k;//預處理所有狀態for(int i=0;i<1<<n;i++)//檢查當前狀態是否合法if(check(i)){legal.push_back(i);cnt[i] = count(i); }//預處理所有合法狀態的合法轉移狀態 for(auto i:legal)for(auto j:legal) if(!(i&j)&&check(i|j)) //上下不相鄰且左右不相鄰trans[i].push_back(j);f[0][0][0] = 1;for(int i=1;i<=n;i++)for(int j=0;j<=k;j++)for(auto st : legal)for(auto tt : trans[st])if(j-cnt[st]>=0)f[i][j][st] += f[i-1][j-cnt[st]][tt];LL ans = 0; for(auto s : legal)ans += f[n][k][s];cout<<ans; return 0; }總結
- 上一篇: gif透明背景动画_PS教程:制作动态界
- 下一篇: 推荐一些Fortran参考书