[SCOI2005]互不侵犯 [SCOI2005]互不侵犯
题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
注:数据有加强(2018/4/25)
输入输出格式
输入格式:
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
输出格式:
所得的方案数
输入输出样例
输入样例#1:
3 2
输出样例#1:
16
由(n le 9)可以看粗应该是个状压DP
所以就可以高高兴兴个设状态了
用(f[i][j][k])表示前(i) 行 最后一行状态为(j)放(k)个的方案数
需要两个函数判断状态合不合法还有放置的个数
inline int bitcnt(int x,int ans=0){
for(;x;x&=(x-1),++ans);
return ans;
}
用来判断二进制中有几个一((x&=x-1)是不是很熟悉没错就是今年初赛题)
inline bool pd(int x){
return (((x<<1)&x)||((x>>1)&x));
}
这个用来判断一个数合不合法(一个1左右不能有另一个1)
然后就可以列状态转移方程了
(displaystyle f[i][j][k]=sum_{l与j合法}f[i-1][l][k-bitcnt(k)])
#include<iostream>
#include<cstdio>
using namespace std;
#define int long long
int mian(){
int s=0,f=1;char ch;
while(!isdigit(ch=getchar()))(ch=='-')&&(f=-1);
for(s=ch-'0';isdigit(ch=getchar());s=s*10+ch-'0');
return s*f;
}
/*
子供の时の梦は言えますか
その梦すら 沟に 舍てたのは
*/
int f[10][(1<<10)+1][100];
int n,m;
inline int bitcnt(int x,int ans=0){
for(;x;x&=(x-1),++ans);
return ans;
}
inline bool pd(int x){
return (((x<<1)&x)||((x>>1)&x));
}
signed main(){
n=mian(),m=mian();
const int U = (1<<n)-1;
f[0][0][0]=1;
for(int i=1;i<=n;++i){
for(int k=0;k<=U;++k){
if(pd(k))continue;
for(int l=0;l<=U;++l){
if(pd(l))continue;
if(l&k)continue;
if((l>>1)&k)continue;
if((l<<1)&k)continue;
// cout<<k<<' '<<l<<endl;
int c=bitcnt(k);
for(int o=c;o<=m;++o)
f[i][k][o]+=f[i-1][l][o-c];
}
}
}
int ans=0;
for(int i=0;i<=U;++i)ans+=f[n][i][m];
cout<<ans<<endl;
return 0;
}