【洛谷P1896】互不侵犯【状压DP】

题目描述

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

输入格式

只有一行,包含两个数 N , K N,K N,K

输出格式

所得的方案数

样例输入 #1

3 2

样例输出 #1

16

提示

数据范围及约定

对于全部数据, 1 ≤ N ≤ 9 1 \le N \le 9 1N9 0 ≤ K ≤ N × N 0 \le K \le N\times N 0KN×N

分析

看数据范围,一眼状压。

关键看阶段和状态怎么设计,一般来讲都是一行一行向下转移,而且影响也只是到下一行,转移显然跟国王个数有关,所以就设 f [ i ] [ j ] [ s ] f[i][j][s] f[i][j][s] 为填了前 i i i 行,已经用了 j j j 个国王,当前行的状态为 s s s 的方案总数。

从上一行转移过来: f [ i ] [ j ] = Σ f [ i − 1 ] [ j − c n t [ i ] ] [ s ′ ] f[i][j]=\Sigma f[i-1][j-cnt[i]][s'] f[i][j]=Σf[i1][jcnt[i]][s]
其中cnt[i]是当前状态1的个数,可以预处理出来。 s ′ s' s 是所有合法状态,判断合法状态只需要左移右移判断“&”是否为0就行。

一个优化,因为同一行当中两个1相邻是不允许的,所以可以预处理同一行中间可行的所有状态,大大缩减枚举。

上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;

int n,k;
ll f[10][90][1<<10],ans;
ll cnt[1<<10],ok[1<<10],tot;

int main()
{
	cin>>n>>k;
	for(int i=0;i<=(1<<n)-1;i++)
	{
		int t=i;
		while(t)
		{
			if(t&1) cnt[i]++;
			t>>=1;
		}
		if(((i<<1)&i)==0&&((i>>1)&i)==0) ok[++tot]=i;//预处理
	}
	f[0][0][0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int h=1;h<=tot;h++)
		{
			int s1=ok[h];//这一行 
			for(int l=1;l<=tot;l++)
			{
				int s2=ok[l];//上一行 
				if((s1&s2)==0&&((s1<<1)&s2)==0&&((s1>>1)&s2)==0)
				{
					for(int j=0;j<=k;j++)
					{
						if(j-cnt[s1]>=0) 
						f[i][j][s1]+=f[i-1][j-cnt[s1]][s2];
					}
				}
			}
		}
	}
	for(int i=1;i<=tot;i++)
	{
		ans+=f[n][k][ok[i]];
	}
	cout<<ans;
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值