[SCOI2005]互不侵犯

洛咕

题意:在(N×N(N<=9)) 的棋盘里面放(M(M<=N*N)) 个国王,使他们互不攻击,共有多少种摆放方案?国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的一个格子.

分析:本题多了一个限制条件就是要求放m个国王.于是设(f[i][j][k]) 表示前i行,放置j个国王,且第i行状态为k的方案数.则(f[i][j][k]=f[i-1][j-num[k]][l]) (其中num[k]表示状态k中有多少位为1)((S[k]&S[l])=0且((S[k]<<1)&S[l])=0且((S[k]>>1)&S[l])=0且j-num[k]>=0)

### 老规矩,又可以麻烦地预处理出集合S,满足状态S[i]的任意两个为1的位不相邻.这样就可以直接枚举有用的状态,优化一下时间.(但其实好像没什么必要,时间复杂度没有优化多少,但是代码长度增加了十几行)

对于每个(S[i]) 我们还可以预处理出它的num[i],意义如上所述.

一定要记得开long long...

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
int n,m,sum,S[1005],num[1005];
LL ans,f[10][100][1005];
inline int count(int x){
    int cnt=0;
    for(int i=0;i<n;i++)
		if((x>>i)&1)cnt++;
    return cnt;
}
int main(){
    n=read(),m=read();
    for(int i=0;i<(1<<n);i++){
		int last=-1,bj=1;
		for(int j=0;j<n;j++){
	    	if((i>>j)&1){
				if(last==-1)last=j;
				else if(j-last<=1){
		    		bj=0;
		    		break;
				}
				else last=j;
	    	}
		}
		if(bj)S[++sum]=i;
    }
    for(int i=1;i<=sum;i++){
		num[i]=count(S[i]);
		f[1][num[i]][S[i]]=1;
    }
    for(int i=2;i<=n;i++)
		for(int j=0;j<=m;j++)
	    	for(int k=1;k<=sum;k++)
				for(int l=1;l<=sum;l++){
		    		if((S[k]&S[l])||((S[k]<<1)&S[l])||((S[k]>>1)&S[l]))continue;
		    		if(j-num[k]>=0)f[i][j][S[k]]+=f[i-1][j-num[k]][S[l]];
				}
    for(int i=1;i<=sum;i++)ans+=f[n][m][S[i]];
    printf("%lld
",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/10976360.html