洛谷同步链接

题目传送门

什么是状压DP

状压 DP 是动态规划的一种,通过将状态压缩为整数来达到优化转移的目的。

——OI Wiki

状态压缩

例如,给定一个 bool 数组 $c$,那么 $c_i$ 为 $0$ 或 $1$。

我们可以将其压缩为一个二进制整数 $(a)_2$,这样 a&(1<<i) 即 $c_i$。可以参照下表:

$i$ 0 1 2 3 4 5 6 7
$c_i$ $0$ $1$ $0$ $1$ $1$ $0$ $0$ $1$
a&(1<<i) $0$ $1$ $0$ $1$ $1$ $0$ $0$ $1$

此时,$(a)_2=(10011010)_2,a=154$。

状压DP

在此基础上进行DP,状态的转移化为了整数,那么状态转移同样得到了优化(位运算)。

设计状态压缩

首先,这是一个 $N\times N$ 的棋盘。

那么,我们考虑压缩一行为一个整数。(当然,压缩列同样可行,你只需要“将棋盘转 $90^\circ$”)

$0$ 表示并没有放置国王,$1$ 表示放置了国王。那么比如 $74=(01001010)_2$ 表示的便是这样的一行:

考虑状态转移

我们定义 $dp[i][j][k]$ 表示:

  • DP至第 $i$ 行;
  • 共放置了 $j$ 个国王(由于题目给出“放置 $K$ 个国王”);
  • 第 $i$ 行状态为 $k$(因为国王会影响到下一行的状态)。

一个很明显的结论是国王周围 $8$ 个格子内不能有其他国王(如图)。

蓝色为国王,红色为不能走区域(橙色为红色重合部分),绿色为其他国王可以放置的位置。

行内预处理

我们可以先处理行内不可能存在的情况:行内国王相邻

也就是说,状态 $S$ 内不能存在两个二进制位 $1$ 相邻。

那么我们便可以通过 (S<<1)|S 判断(假设 $S=(110101)_2$):

S 0 1 1 0 1 0 1
S<<1 1 1 0 1 0 1 0

不难发现,如果此时有 $1$ 在同一列,那么 $S$ 便会有相邻的 $1$。

事实上,使用 (S>>1)|S 也是可行的,但这不重要。因为两个相邻的 $1$,无论左移还是右移,都是从至少两位 $1$ 重合变为至少一位 $1$ 重合

所以,当一个状态 $S$ 满足 (((S<<1)|S)&1)==0 时,这一行才可能成立,其余情况可以直接忽略

于是,开一个 $ok$ 数组记录,$ok[i]$ 表示第 $i$ 大的合法行

其实还需要预处理一个 $cnt[S]$ ,具体后文会提到。

行间转移

处理完了行内,现在开始处理行间状态转移。

如果第 $i-1$ 行第 $j$ 号位置有一个国王,那么第 $i$ 行第 $j-1,j,j+1$ 号位置都不能有国王。

不妨令第 $i$ 行状态为 $S1$,第 $i-1$ 行为 $S2$。

同样的,由于我们进行了状态压缩,也就是当状态满足 ((S2<<1)&S1)==0&&(S2&S1)==0&&((S2>>1)&S1)==0 时,第 $i$ 行才有可能为状态 $S1$。

对条件进行简写:(((S2<<1)|S2|(S2>>1))&S1)==0

转移方程至此也就很好理解了:

$$dp[i][j][S1]=\sum_{j=cnt[S1]}^k{dp[i-1][j-cnt[S1]][S2]}$$

其中,$cnt[S1]$ 表示二进制整数 $S1$ 中 $1$ 的个数,已经预处理过。

DP边界

$$dp[0][0][0]=1$$

很好理解,就是第 (0) 行无法处理,只能放置 $0$ 个国王,第 $-1$ 行无法放置国王。

但是,事实上多数情况都是为了运算方便而如此设计的。

统计答案

首先 $dp$ 数组的前两维很好判断,就是 $n,k$。那么第三维,第 $n$ 行的状态我们是不知道的。事实上为了统计所有可能数,第 $n$ 行什么状态都有可能,因此我们一个一个枚举累加即可。

注意事项

  1. long long!开 long long!开 long long

    重要的事情说三遍。

  2. 数组大小

    $dp$ 需要开到 $dp[N+1][K+1][2^{N+1}]$。

    其中 $N,K$ 需要取最大值 $N=9,K=N^2=81$。

    $ok,cnt$ 都需要开到 $2^{N+1}$。

    考虑到数据范围 $N\leq9$,并不会 $\text{MLE}$。

  3. 运算优先级

    参见OI Wiki可以发现 &^| 这三个位运算符的优先级低于 == 等关系运算符。因此,建议对于位运算符,尽量都加上括号保证运算顺序正确

AC代码

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
const int N=9,K=N*N;
int n,k,top;
//1:国王,0:空置
//cnt[i]:i在二进制下的 1 的个数
//ok[i]:第i个行内1不相邻的状态
//dp[i][j][k]:第i行,共放置j个国王,第i行是状态k
ll cnt[(1<<N+1)+1],ok[(1<<N+1)+1],dp[N+1][K+1][(1<<N+1)+1];
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/ scanf("%d %d",&n,&k);
//预处理:cnt,ok.
for(int i=0;i<(1<<n);i++){
int pl=0,j=i;
while(j){
if(j&1)pl++;
j>>=1;
}cnt[i]=pl;
if(((i<<1)&i)==0)ok[++top]=i;
}//边界
dp[0][0][0]=1;
for(int i=1;i<=n;i++){
//p,q:枚举第i行,第i-1行的状态
//[1,top]优化,剔除行内不满足的
for(int p=1;p<=top;p++){
int s1=ok[p];
for(int q=1;q<=top;q++){
int s2=ok[q];
//行间合法
if((((s2<<1)|s2|(s2>>1))&s1)==0){
//DP累加
for(int j=cnt[s1];j<=k;j++){
dp[i][j][s1]+=dp[i-1][j-cnt[s1]][s2];
}
}
}
}
}//输出
ll ans=0;
for(int i=1;i<=top;i++)ans+=dp[n][k][ok[i]];
printf("%lld\n",ans); /*fclose(stdin);
fclose(stdout);*/
return 0;
}

题解:[SCOI2005] 互不侵犯的更多相关文章

  1. SCOI2005互不侵犯King

    1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1499  Solved: 872[Submit][S ...

  2. 洛谷 P1896 [SCOI2005]互不侵犯

    洛谷 P1896 [SCOI2005]互不侵犯 题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8 ...

  3. BZOJ 1087: [SCOI2005]互不侵犯King [状压DP]

    1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3336  Solved: 1936[Submit][ ...

  4. 洛谷1377 M国王 (SCOI2005互不侵犯King)

    洛谷1377 M国王 (SCOI2005互不侵犯King) 本题地址:http://www.luogu.org/problem/show?pid=1377 题目描述 天天都是n皇后,多么无聊啊.我们来 ...

  5. 1087: [SCOI2005]互不侵犯King

    1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4276  Solved: 2471[Submit][ ...

  6. BZOJ1087 SCOI2005 互不侵犯King 【状压DP】

    BZOJ1087 SCOI2005 互不侵犯King Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附 ...

  7. bzoj 1087 [SCOI2005]互不侵犯King 状态压缩dp

    1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MB[Submit][Status][Discuss] Descripti ...

  8. 状压DP【洛谷P1896】 [SCOI2005]互不侵犯

    P1896 [SCOI2005]互不侵犯 题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子 ...

  9. 洛谷P1896 [SCOI2005]互不侵犯King

    P1896 [SCOI2005]互不侵犯King 题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共 ...

  10. BZOJ 1087:[SCOI2005]互不侵犯King(状压DP)

    [SCOI2005]互不侵犯King [题目描述] 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子 ...

随机推荐

  1. 使用 GitHub Actions 构建 CosyVoice 项目的运行环境镜像并推送到阿里云容器镜像服务和 GitHub Package Registry

    使用 GitHub Actions 构建 CosyVoice 项目的运行环境镜像并推送到阿里云容器镜像服务和 GitHub Package Registry 概述 本文介绍了如何使用 GitHub A ...

  2. 【Uber 面试真题】SQL :每个星期连续5星评价最多的司机

    大家好,我是"蒋点数分",多年以来一直从事数据分析工作.从今天开始,与大家持续分享关于数据分析的学习内容. 本文是第一篇,也是[SQL 周周练]系列的第一篇.该系列是挑选或自编具有 ...

  3. 从 UEFI 启动到双系统——记一次双系统 Linux 分区迁移

    前言 我的台式电脑上,装了 Windows 和 Linux 双系统. 我有两块 1 TB 硬盘,就把它们叫作硬盘 0 和硬盘 1 吧.最开始的时候,硬盘 0 上装了 Windows 系统,而我的数据分 ...

  4. 猫映射(Arnold变换),猫脸变换介绍与基于例题脚本的爆破

    前置信息 http://www.jiamisoft.com/blog/index.php/7249-erzhituxiangjiamisuanfaarnold.html https://mp.weix ...

  5. 使用ajax来进行登录验证

    servlet: 1 @WebServlet("/login.do") 2 public class AjaxLoginServlet extends HttpServlet { ...

  6. 深入探讨控制反转(IOC)与依赖注入(DI)模式原理与应用实践

    本文由 ChatMoney团队出品 在软件开发中,控制反转(Inversion of Control,简称IOC)和依赖注入(Dependency Injection,简称DI)是两种常用的设计模式, ...

  7. Opencv学习:回到原点!关于一些基础的函数

    opencv简单的图片读取和显示 1.图像读取  Mat img = imread("C:/clip.png", 1); imshow("fang2", img ...

  8. 数栈SQL优化案例:OR条件优化

    本文整理自:袋鼠云技术荟 | SQL优化案例(2):OR条件优化 数栈是云原生-站式数据中台PaaS,我们在github上有一个有趣的开源项目:https://github.com/DTStack/f ...

  9. ABB机器人指令 PackRawBytes

    参数: Value, RawData \Network , StartIndex ,\Hex1|IntX|\Float4|\ASCII; Value: 需要打包的数据, 类型包含num.dnum, b ...

  10. 阻止vue组件vuedraggable在使用时打开浏览器新标签

    前言 浏览器在文字拖动时会打开链接,图片拖动时打开新窗口,这是浏览器的特性. vue-draggable组件就是需要拖动的,就与这个特性契合了,但大多时候在项目中我们不需要这个特性. 解决方式 需要在 ...