【题解】P1896 [SCOI2005]互不侵犯

\(Description:\)

题面类似n皇后,不过摆的是国王

\(Sample\) \(Input:\)

3 2

\(Sample\) \(Output:\)

16

\(Solition:\)

设计状态,这种题我还是没有思路啊,怎么办啊啊啊啊啊?

看了看题解:

设计 \(f[i][j][k]\) 表示前 \(i\) 行,该行状态为 \(j\),前 \(i\) 行共放了 \(k\) 个国王的方案数

那么转移方程应该就是:(设 \(Z\) 是我们所有该行的状态,\(num\) 是这该状态要放几个国王

\(f[i][j][l]=(check(l,t))\sum_{t\subseteq Z} f[i-1][t][l-num[j]] (num[j]<=l<=k)\)

转移方程发现是可以满足无后效性的,这个时候确定,可以dp。

那么发现其实这个 \(check\) 可以快速求出

只要这个状态 \(l\) 和上一个状态 \(t\) 左上和当前和右上没有相同的 \(1\) 相交就可以了。

那么初始化很明显,第 \(1\) 行要用到第 \(0\) 行的状态,第 \(0\) 行除了全 \(0\) 这种状态,其他全都为 \(1\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,number,cnt,ans;
const int N=9+1,M=(1<<9)+1,K=9*9+1;
int s[M],f[N][M][K],num[M];
inline int lowbit(int x){
	return x&(-x);
}
inline bool check(int x,int y){
	if(x&y) return 0;
	if(x&(y<<1)) return 0;
	if(x&(y>>1)) return 0;
	return 1;
}
signed main(){
	scanf("%lld%lld",&n,&number);
	for(int i=0;i<(1<<n);++i){
		if(i&(i<<1)) continue;
		int tmp=i;
		s[++cnt]=i;
		while(tmp) { num[cnt]++;tmp-=lowbit(tmp);}
	}
	f[0][1][0]=1;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=cnt;++j)
			for(int k=num[j];k<=number;++k)
				for(int l=1;l<=cnt;++l)
					if(check(s[l],s[j]))
						f[i][j][k]+=f[i-1][l][k-num[j]];
	for(int i=1;i<=cnt;++i)
		ans+=f[n][i][number];
	printf("%lld\n",ans);
	return 0;
}

posted @ 2019-04-15 20:42  章鱼那个哥  阅读(118)  评论(0编辑  收藏  举报