【BZOJ】1087 互不侵犯King

【解析】狀壓dp

[分析]
參考:http://blog.sina.com.cn/s/blog_8442ec3b0100xgpb.html
由於狀態比較多,首先預處理出只考慮這一行滿足要求的狀態,並求出c[i]表示這個狀態含1的個數。
設f[i][j][k]表示前i行,共放了j個,第i行的放置情況爲k的方案數。
初始狀態:f[0][0][(00...0)]=1。
動態轉移方程:f[i][j][k]=sum{f[i-1][j-c[k]][l]},legal(l,k)=true。這裏的legal(l,k)即檢查相鄰兩行有沒有問題。
所求答案:sum{f[n][kk][l]}。

[小結]這種要滿足包括了當前行和之前行的狀壓dp,先進行預處理減少狀態。

[代碼]
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

typedef long long LL;
const int N=10;
const int K=100;

int n,kk;
int p[1<<N],c[1<<N];
LL f[N][K][1<<N],res;

inline void pre(int now)
{
	for (int i=1;i<=n-1;i++)
		if (now&1<<i-1&&now&1<<i) return;
	p[++p[0]]=now;
	for (int i=1;i<=n;i++) c[p[0]]+=(now&1<<i-1)>0;
}

inline int check(int k,int l)
{
	for (int i=1;i<=n;i++)
		if (k&1<<i-1)
		{
			if (l&1<<i-1) return 0;
			if (i^1&&l&1<<i-2) return 0;
			if (i^n&&l&1<<i) return 0;
		}
	return 1;
}

int main(void)
{	
	scanf("%d%d",&n,&kk);
	for (int i=0;i<1<<n;i++) pre(i);
	
	f[0][0][1]=1;
	for (int i=1;i<=n;i++)
		for (int k=1;k<=p[0];k++)
			for (int j=c[k];j<=kk;j++)
				for (int l=1;l<=p[0];l++)
					if (check(p[k],p[l]))
						f[i][j][k]+=f[i-1][j-c[k]][l];
	for (int i=1;i<=p[0];i++) res+=f[n][kk][i];
	printf("%lld\n",res);
	
	return 0;
}

發佈了137 篇原創文章 · 獲贊 4 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章