// 題意:在一個n * n的棋盤上,放k個棋子使得每個棋子的周圍八個區域都不得有其他棋子,問這樣放置共有多少種
// 方法:狀壓+dp的水題 這幾天都要被狀壓弄瘋了
// dp[i][j][k] 代表第i行狀態爲j棋盤上總共放了k個棋子的方案數
// 然後由此行的狀態j推出下一行不衝突的狀態l就好了
// 巧妙利用 位運算和與運算來判斷衝突 又快又方便
#include "iostream"
#include "string.h"
using namespace std;
int n, m;
long long dp[11][1<<10][105];
long long cnt[(1<<10)];
int calcu(int x)
{
int ans = 0;
for(int i = 0; i < 10; i++)
if(x & (1<<i)) ans++;
return ans;
}
int main()
{
for(int i = 0; i < (1<<10); i ++)
cnt[i] = calcu(i);
while(cin>>n>>m)
{
if(n%2 == 0 && m > (n * n / 4)) { //自作主張 稍微剪枝下 如果n是偶數 m不得超過(n*n)/2
cout<<0<<endl;
continue;
}
int maxn = (1<<n) - 1;
memset(dp, 0, sizeof dp);
for(int i = 0; i <= maxn; i++)
{
if(i & (i<<1)) continue;
if(cnt[i] <= m)
dp[1][i][cnt[i]] = 1;
}
for(int i = 1; i <= (n - 1); i++) //處理到第I行
{
for(int k = 0; k <= m; k++) //已經放了k個棋子
for(int j = 0; j <= maxn; j ++) //當前行的狀態爲j
{
if(j & (j<<1)) continue; //如果本行的狀態不合法直接跳過
for(int l = 0; l <= maxn; l++) //枚舉下一行的狀態l
{
if(l & (l<<1)) continue; //如果狀態l不合法直接跳過
if((l & j) || (l & (j<<1)) || (l & (j>>1))) continue; //如果兩狀態衝突
int t2 = cnt[l]; //計算出下一行放置棋子的數量
if(k + t2 < m) //如果當前所有放置棋子總和小於m
dp[i + 1][l][k + t2] += dp[i][j][k];
else if(k + t2 == m)
dp[i + 1][l][m] += dp[i][j][k];
}
}
}
long long ans = 0;
for(int j = 0; j <= maxn; j++) //把最後一行所有可行狀態且總共放置m個棋子的所有方案數相加輸出即可
ans += dp[n][j][m];
cout<<ans<<endl;
}
return 0;
}
SGU 223(狀壓+dp)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.