P1896 [SCOI2005]互不侵犯(状压DP)

 

P1896 [SCOI2005]互不侵犯

题目描述

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

注:数据有加强(2018/4/25)

输入输出格式

输入格式:

 

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

 

输出格式:

 

所得的方案数

 

::::::

题目十分简短:观察数据范围我们可以知道:这题有庞大的状态量,所以我们就用状压DP解决问题

dp思路:三维,第一维表示行数,第二维表示状态(二进制),第三维表示已经放了的棋子数(说实话做题做多了会有套路的,有数量限制的dp一般都要开一维表示用了的数量)

WA点:long long型

代码:

/***********************************************/
const int maxn= 1<<10;
ll d[12][maxn][120];//当第i行为j方案时,放了z个国王,前i行的方案数 
int can[maxn];

int sum_1(ll n)//计算n二进制中1的个数
{
	int ans=0;
	while(n)
	{
		if( (n&1) >0 ) ans++;
		n=(n>>1);
	}
	return ans;
}

bool is(int n,int m)
{
	if( (n&m)==0 && (n&(m<<1))==0 && (n&(m>>1))==0 ) return true;
	return false;
}

int main()
{
	int n,k;
	cin>>n>>k;
	ll mos=(1<<(n))-1;
	for(ll i=0;i<=mos;i++)
		if( ( i & (i<<1) ) ==0 && ( i & (i>>1) ) ==0 )
			can[i]=1;
	for(ll j=0;j<=mos;j++)
		if( can[j] && sum_1(j)<=k ) d[1][j][sum_1(j)]=1;
	for(int i=2;i<=n;i++)
		for(ll j=0;j<=mos;j++) //暴力每种方案 
			if( can[j] )
				for(ll k1=0;k1<=mos;k1++) //对前一行
					if( can[k1] && is(j,k1) ) //is:与前一行是否冲突
						for(ll l=k;l>=sum_1(j);l--)  //此循环表示枚举第i行及之前行所有的国王数
							d[i][j][l]+=d[i-1][k1][l-sum_1(j)];	
	ll ans=0;
	for(ll i=0;i<=mos;i++) ans+=d[n][i][k];
	cout<<ans<<endl;
    return 0;
}

  

 

posted @ 2019-01-28 09:57  liuyongliu  阅读(435)  评论(0)    收藏  举报