洛谷同步链接

题目传送门

什么是状压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. 一些软件、jar包下载链接、方法

    目录 jar包下载 dbutils C3P0 软件下载 TeamViewer 远程桌面 EV录屏 SublimeText 编辑器 feiQ 通信 文件共享 jdk 8u171 下载 jar包下载 db ...

  2. 史上最强大的 Python 脚本

    Python 不仅仅是初学者教程和简单数据脚本的语言,它更像是现代的魔法书.有些人编写的"咒语"如此强大,仿佛打破了现实的规则. 我们谈论的是能够超越人类.渗透机器.甚至自我重写的 ...

  3. RAG越来越不准?你可能忽略了“元数据”的力量

    你是否也有这样的困扰? 问大模型一个很具体的问题:"请告诉我A软件的安装方法." 结果它却信誓旦旦地告诉了你B软件的安装步骤. 在这个过程中,你可能已经花了大量时间解析和清洗上千份 ...

  4. K8stools工具

    简介 K8stools 是一个 Kubernetes 日常运维辅助工具集,旨在提升运维效率,辅助平台治理与资源优化.功能涵盖资源分析.趋势评估.异常检测.行为采集.成本估算等常见场景,适用于 DevO ...

  5. 操作系统:Linux如何实现进程与进程调度

    Linux如何表示进程 在Cosmos中,设计了一个thread_t数据结构来代表一个进程,Linux也同样是用一个数据结构表示进程. Linux进程的数据结构 在Linux系统下,把运行中的应用程序 ...

  6. Nmap 从入门到精通:详细指南

    Nmap 从入门到精通:详细指南 1. Nmap 是什么? Nmap(Network Mapper)是一款开源的网络探测和安全审计工具,广泛用于以下场景: 主机发现:识别网络中的活动设备. 端口扫描: ...

  7. python时间戳转时间格式

    一.两种时间戳转换为时间格式:13位和10位,将时间戳转成时间格式 import time #13位时间戳转时间 tre_timeArray = time.localtime(164601220668 ...

  8. MCP Server On FC 之旅第四站: 长连接闲置计费最高降低87%成本的技术内幕

    函数计算( FC )是阿里云事件驱动的全托管计算服务, 使用函数计算,您无需采购与管理服务器等基础设施,只需编写并上传代码或镜像.函数计算为您准备好计算资源,弹性地.可靠地运行任务,并提供日志查询.性 ...

  9. JuiceFS v1.3-Beta2:集成 Apache Ranger,实现更精细化的权限控制

    在大数据场景中,文件系统和应用组件的权限管理至关重要.在最新发布的 JuiceFS 社区版 v1.3-Beta 2 中,JuiceFS 引入了与 Apache Ranger 的集成,提供了更为灵活和细 ...

  10. golang中写个字符串遍历谁不会?且看我如何提升 50 倍

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 引子 VictoriaMetrics (Github: h ...