习题:国王(状压DP)

题目:

B. 国王

内存限制:64 MiB时间限制:500 ms标准输入输出

题目类型:传统评测方式:文本比较

题目描述

原题来自:SGU 223

在 n*n的棋盘上放k  个国王,国王可攻击相邻的 8个格子,求使它们无法互相攻击的方案总数。

输入格式

只有一行,包含两个整数 n 和k 。

输出格式

每组数据一行为方案总数,若不能够放置则输出 0。

样例

样例输入 1

3 2

样例输出 1

16

样例输入 2

4 4

样例输出 2

79

思路:
因为n小,所以第一选择就是搜索或者状压DP,因为每一个各自的状态为放或者不放,总的时间复杂度为O(2^N^2)

之后考虑状压DP ,设dp[i][j][k],为第i行状态为j一共有k个国王,j状态指将j转化成二进制,每一位上的数是1就代表放,

反之则代表不放,

考虑冲突问题

就是上一行的状态右移或者左移再并,

最后统计一下最后一行的答案即可

还有一些小优化,因为每一行的状态的总数是一定的,枚举每一个状态,也就是1~2^n时,

有些状态是不行的,

此时我们将这些可行的状态记录下来

代码

#include<iostream>
using namespace std;
int n,num_king;
long long dp[15][1050][105];
int a[1050];
int num[1050];
int tot;
long long ans;
int main()
{
	cin>>n>>num_king; 
	for(int i=0;i<(1<<n);i++)
	{
		if(i&(i<<1))
			continue;
		int t=i;
		a[++tot]=i;
		while(t)
		{
			num[tot]+=t%2;
			t/=2;
		}
	}
	dp[0][1][0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=tot;j++)
		{
			for(int k=0;k<=num_king;k++)
			{
				if(k>=num[j])
				{
					for(int l=1;l<=tot;l++)
					{
						if(!(a[l]&a[j])&&!(a[l]&(a[j]<<1))&&!(a[l]&(a[j]>>1)))
						{
							dp[i][j][k]+=dp[i-1][l][k-num[j]];
						}	
					}
				}
			}
		}
	}
	for(int i=1;i<=tot;i++)
	{
		ans+=dp[n][i][num_king];
	}
	cout<<ans;
	return 0;
} 

 

posted @ 2019-07-29 19:53  loney_s  阅读(296)  评论(0)    收藏  举报