前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >洛谷 P1896 [SCOI2005]互不侵犯(状压dp)

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

作者头像
glm233
发布2020-09-28 11:48:12
6680
发布2020-09-28 11:48:12
举报
P1896 [SCOI2005]互不侵犯

题目描述

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

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

输入格式

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

输出格式

所得的方案数

输入输出样例

输入 #1

3 2

输出 #1

16

思路:状压dp,

首先 先解释一下状压是什么吧 对于这种类型的题 每个点 都只存在两种情况 即为放了东西 没放东西 (这里先解释一下为什么不可以把状态设为每一个点 因为 如果设为每一个点的话 状态很不好转移 下一个点的位置无法确定)那么 我们就可以把放了东西定为1 没放东西定为0 于是 我们便可以得到类似“0 1 0 1”这样的一个状态(这里我们只模拟了一行的状态) 而这样的一个状态“0 1 0 1” 可以把他看作一个二进制数 我们也就可以很轻松的把他转换为一个10进制数 即5 那么 这个十进制数5 就可以表示这样的一个“0 1 0 1”的状态 也就是说 我们把这样的一个状态压缩到了一个十进制数里面 也就是状态压缩了

而我们的DP 就是通过状态的转移来进行计算 那么我们这个时候 所需要存储的状态 就会变成一个数 避免了原先一整行的摆放情况难以存储的问题

于是我们看回这道题 首先 它就是刚刚讲过的 是一个判断摆放情况的题 而且 他的数据范围很小(数据范围大了 状压就不一定能做了 因为它有一个进制的转换) 所以 可以判断出来 这是一道状压DP

那么 我们就可以设状态为每一行的摆放情况 f(i,j,k) 用来表示 第i行 摆放情况为j 截止当前行 使当前状态下一共摆放的个数为k的情况总数

那么 他的状态怎么转移呢 因为它本质上是一个放或不放的题 所以他的动态转移方程类似于01背包

f[i][j][k]=f[i][j][k]+f[i-1][t][k-第i行放了的个数] 其中 t为第i-1行的状态

那么我们只需要枚举行 枚举该行状态 枚举上一行状态 再判断一下两状态是否冲突 然后再统计答案就好啦

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rg register ll
#define inf 2147483647
#define lb(x) (x&(-x))
ll sz[200005],n,k1;
template <typename T> inline void read(T& x)
{
    x=0;char ch=getchar();ll f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}x*=f;
}
inline ll query(ll x){ll res=0;while(x){res+=sz[x];x-=lb(x);}return res;}
inline void add(ll x,ll val){while(x<=n){sz[x]+=val;x+=lb(x);}}//第x个加上val
ll dp[10][1000][20],cost[1000],sit[1000],cnt;//sit[i]为第i种状态的二进制表示,cost[i]表示第i种状态的用1个数
inline void dfs(int state,int sum,int column)
{
	if(column>=n)
	{
		sit[++cnt]=state;
		cost[cnt]=sum;
		return ;
	}
	dfs(state,sum,column+1);
	dfs(state+(1<<column),sum+1,column+2);
}
int main() 
{
	cin>>n>>k1;
	dfs(0,0,0);
	for(int i=1;i<=cnt;i++)dp[1][i][cost[i]]=1;
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<=cnt;j++)
		{
			for(int k=1;k<=cnt;k++)
			{
				if(sit[j]&sit[k])continue;
				if(sit[j]&(sit[k]<<1))continue;
				if((sit[j]<<1)&sit[k])continue;
				for(int m=k1;m>=cost[j];m--)dp[i][j][m]+=dp[i-1][k][m-cost[j]];
			}
		}
	}
	ll ans=0;
	for(int i=1;i<=cnt;i++)ans+=dp[n][i][k1];
	cout<<ans<<endl;
    return 0;
    
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • P1896 [SCOI2005]互不侵犯
  • 题目描述
  • 输入格式
  • 输出格式
  • 输入输出样例
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档