(状压dp)洛谷P1896互不侵犯

洛谷P1896互不侵犯

思路:

DP好难,状压DP好难
状压dp就是把状态压缩成一个数,以节省空间。例如这题,国王在一行中要么放要么不放,那我们令放为1,不放为0,就可以组成一个01串,我们把这01串看成一个二进制数的话,就可以将这个状态表示成一个数。
因为涉及到二进制,所以状态dp中采用很多位运算。
对于题目要求国王四周都不能有其他的国王。
在同一行时,我们可以用((s>>1)&s)==0 && ((s<<1)&s)==0来判断,因为如果有两个1相邻,那么这个数左移或者右移后和他本身的按位与肯定不为0。我们也可以简化写法为(((s>>1)|(s<<1))&s)==0。
对于不同行,如上,我们可以得到(((s1|(s1>>1)|(s1<<1))&s2)==0。
用dp[len][sta][s]表示第len行在s状态下,用了sta个国王的结果,那么最后我们求得答案就是 ∑ ∑ dp[n][k][si]。
我们先初始化好同一行符合要求得状态,这样可以减少枚举和判断次数。
dp[0][0][0]=1可以理解为在第0行,放0个棋子的状态肯定只有一种。
虽然看起来很简单的东西,但是我看了好多题解才懂(真是太菜了)。感觉dp就是看题目的时候一点思路都没有,看了题解就觉得这是水题?QAQ

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
const int N=1e6+10;
const int mod=1e7+9;
const int maxn=0x3f3f3f3f;
const int minn=0xc0c0c0c0;
const int inf=99999999;
using namespace std;
ll sta[2000],s[2000],dp[10][100][1<<11];
int n,k,len=0;
void pre()
{
	int i;
	for(i=0;i<(1<<n);i++)
	{
		int sum=0,x=i;
		while(x)
		{
			if(x&1)	
				sum++;
			x>>=1;
		}
		sta[i]=sum;
		if((((i>>1)|(i<<1))&i)==0)
			s[++len]=i;
	}
	dp[0][0][0]=1;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>k;
	pre();
	int i,j,l,t;
	for(i=1;i<=n;i++)
		for(j=1;j<=len;j++)
			{
				int x=s[j];
				for(l=1;l<=len;l++)
				{
					int y=s[l];
					if(((y|(y>>1)|(y<<1))&x)==0)
						for(t=0;t<=k;t++)
							if(t-sta[x]>=0)
								dp[i][t][x]+=dp[i-1][t-sta[x]][y];
				}
			}
	ll ans=0;
	for(i=1;i<=len;i++)
		ans+=dp[n][k][s[i]];
	cout<<ans<<endl;
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值