bzoj1087[SCOI2005]互不侵犯King 经典状压dp

DP百题进度:8/100

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1087

1087: [SCOI2005]互不侵犯King

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 4488   Solved: 2599
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

  方案数。

Sample Input

3 2

Sample Output

16

HINT

一道十分经典的状态压缩dp题,题解应该遍地都是吧_(:з」∠)_

但是鉴于本渣状压dp实在是太弱了,所以还是写一篇加深印象吧。

首先能想到dp[i][j]表示前i行放了j个国王的方案数,但很显然我们无法维护上一行对当前行的影响,所以要加一维二进制数state来维护上一行的状态。

通过枚举第一行的状态和当前行的状态,我们可以推出转移方程:dp[i][j][state]+=dp[i-1][j-num][pre];

然后就很好写了,注意判断状态不合法的情况。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int read()
{
	char c;int sum=0,f=1;c=getchar();
	while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
	return sum*f;
}
int n,k;
LL dp[10][90][1<<10],ans;
int count1(int x)
{
	int ans=0;
	while(x)
	{
		if(x&1)
		ans++;
		x>>=1;
	}
	return ans;
}
bool check(LL x,LL y)
{
	if(x&(y<<1))
	return false;
	if(x&(y>>1))
	return false;
	if(x&y)
	return false;
	return true;
}
void DP()
{
	dp[0][0][0]=1;
	for(int i=1;i<=n;i++)
	for(int j=0;j<=k;j++)
	for(int state=0;state<=(1<<n)-1;state++)
	{
		int num=count1(state);
		if(num>j) continue;
		if(state&(state>>1)) continue;
		for(int pre=0;pre<=(1<<n)-1;pre++)
		{
			if(!check(pre,state)) continue;
			if(pre&(pre>>1)) continue;
			dp[i][j][state]+=dp[i-1][j-num][pre];
		}
	}
}
int main()
{
	n=read();k=read();
	DP();
	for(int state=0;state<=(1<<n)-1;state++)
	ans+=dp[n][k][state];
	cout<<ans<<endl;
	return 0;
}
妈耶,状压dp不是方程极难想就是代码极难写。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值