【题解】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;
}