当前位置: 首页 > news >正文

状压 dp --- 棋盘覆盖问题

hello,大家好,今天是 2025 年 9 月 5 日,我们继续开启状压 dp 的学习。

今天我要来给大家介绍的是状压 dp 中的一类经典问题 --- 棋盘覆盖问题

和 TSP 问题一样,棋盘覆盖问题也很好识别,解决方式也相对固定。

一:棋盘覆盖问题简介

在这类问题中,一般针对的是一个二维棋盘

需要在一些限定条件下,用棋子覆盖棋盘,最终求得所有合法方案的数目;

棋盘一般比较小,在 12 * 12 以内;(棋盘覆盖问题很好识别)

但是,暴搜的话一般会超时,因为总的方案数一般很大,通过暴搜枚举所有的方案会超时

如果是 10 * 10 的棋盘,一个格子一个格子的考虑,针对每一个格子放或者不放,就有2^100种      情况。

二:棋盘覆盖问题模板题

题目一:互不侵犯

题目链接:互不侵犯

【题目描述】

【算法原理】

通过模拟测试用例,我们发现,3 * 3 的棋盘放 2 个国王就会出现 16 种情况,因此,棋盘覆盖问题的结果可能会很大,我们要记得开 longlong。

我们不难发现一个性质:如果从上往下填写的是第 i 行,那么只会有第 i - 1 行会影响第 i 行的填写,因此,只要我们确定了第 i - 1 行的填写方式,就能得出第 i 行的填写方式。

我们考虑一下使用动态规划来解决这个问题:

状态压缩:我们把某一行的摆放方案用整数的二进制表示。

1.状态表示:

f[i][j][x]:表示:当前摆到了第 i 行,一共摆放了 j 个棋子,第 i 行的摆放方案为 x 时,所有的合法方案数。

2.状态转移方程:

我们根据最近的一步来划分问题:

假设第 i - 1 行的摆放方式为 y,c[x] 表示 x 的二进制表示中,一共有多少个 1。

状态转移方程成立的前提是,x 和 y 这两个状态不能冲突。那么如何保证这两行的摆放方式不会冲突呢?

我们结合位运算的知识就可以轻松搞定:

推到完状态转移方程之后,我们思考一下时间复杂度:

一共要遍历 n 行,最多要防止 n * n 个棋子,本行的放置方案有 2 ^ n 种,上一行的放置方案也有 2 ^ n 种,因此如果不做任何优化的话,时间复杂度为 O(n * n * n * 2 ^ n * 2 * n),肯定是会超时的。

我们考虑一下优化,我们可以提前将每一行所有的合法方案全部预处理出来,这样的话最后就可以只枚举所有的合法方案了。

每一行的合法方案只需要保证和其余的国王左右互不相邻就可以了。

我们还是使用位运算的知识来预处理:

3.初始化:

由于本体是求方案数的模型,因此 f[0][0][0] = 1 即可。

4.填表顺序:

仅需保证 i 从小到大即可。

5.最终结果:

根据状态表示,以及题目要求,我们的最终结果为:第 n 行时所有 f 表中 j == k 的值相加。

【代码实现】

#include <iostream>
#include <vector>using namespace std;typedef long long LL;
const int N = 11;int n, k;
vector<int> st; // 存行的合法摆放方案
int c[1 << N]; // c[i] 表示:i 这个数的二进制表示中,1 的个数 
LL f[N][N * N][1 << N];int main()
{cin >> n >> k;// 预处理所有的合法方案,以及合法方案中 1 的个数for(int s = 0; s < (1 << n); s++){if(s & (s >> 1)) continue;st.push_back(s);for(int i = 0; i < n; i++)if((s >> i) & 1)c[s]++;} f[0][0][0] = 1;for(int i = 1; i <= n + 1; i++) // 枚举行 for(int j = 0; j <= k; j++) // 枚举棋子数目for(auto x : st) // 最后一行的方案for(auto y : st) // 上一行的方案{if((j < c[x]) || (x & y) || (x & (y >> 1)) || (x & (y << 1))) continue;f[i][j][x] += f[i - 1][j - c[x]][y];} //	LL ret = 0;
//	for(auto s : st) ret += f[n][k][s];
//	cout << ret << endl;cout << f[n + 1][k][0] << endl;return 0;
}

题目二:Corn Fields G

题目链接:Corn Fields G

本题是一道二倍经验的题目,算法原理和上一题基本类似。希望大家能够自己尝试解决之后,再去看下面的内容。

【题目描述】

【算法原理】

解法:状压 dp

1.状态表示:

f[i][x]表示:第 i 行的摆放方案为 x 时,所有合法方案的总数。

2.状态转移方程:

假设第 i - 1 行的摆放方式为 y,当 x 和 y 不冲突时:

f[i][x] += f[i - 1][y];

如何判断 x 和 y 是否冲突???

如果 x 和 y 执行按位与运算,最终结果等于 0,说明不冲突。最终结果不等于 0,说明会有冲突。

优化:我们可以提前预处理处每一行的合法方案:(考虑土地贫瘠情况)

接下来的三步和题目一类似,这里就不再多说了~~~

3. 初始化 f[0][0] = 1;

4. 填表顺序:保证 i 从大到小枚举即可。

5. 最终结果:根据状态表示,以及题目要求,最终结果为 f[n + 1][0]。

【代码实现】

#include <iostream>
#include <vector>using namespace std;typedef long long LL;
const int N = 14, mod = 1e8;int m, n;
vector<int> st[N];
int a[N];LL f[N][1 << N];int main()
{cin >> m >> n;for(int i = 1; i <= m; i++){for(int j = 0; j < n; j++){int x; cin >> x;if(x) a[i] |= (1 << j);}// 预处理当前这一行所有的合法状态for(int s = 0; s < (1 << n); s++){if((s & (s >> 1)) || ((s & a[i]) != s)) continue;st[i].push_back(s);} }f[0][0] = 1; st[0].push_back(0); st[m + 1].push_back(0);for(int i = 1; i <= m + 1; i++)for(int x : st[i])for(int y : st[i - 1]){if(x & y) continue;f[i][x] = (f[i][x] + f[i - 1][y]) % mod; }cout << f[m + 1][0] << endl;return 0;
}

好的,今天的分享就先到这里了,大家晚安!!!


文章转载自:

http://k9oQkpz3.skmpj.cn
http://Oxe8USVT.skmpj.cn
http://XsgOEYTu.skmpj.cn
http://4AcujNQS.skmpj.cn
http://LXHgQJNz.skmpj.cn
http://pMgir8VK.skmpj.cn
http://KL54HoUv.skmpj.cn
http://TwEp1uKM.skmpj.cn
http://2UoETHfA.skmpj.cn
http://LKaNfMua.skmpj.cn
http://xW65w8GM.skmpj.cn
http://CE1ZDmYp.skmpj.cn
http://ibQVk8OM.skmpj.cn
http://NWHfawQL.skmpj.cn
http://mNiizJ5E.skmpj.cn
http://yJW1N5Jw.skmpj.cn
http://QByMF2EE.skmpj.cn
http://BYtvJFsr.skmpj.cn
http://VyioID16.skmpj.cn
http://kYRO7hYL.skmpj.cn
http://mgal1018.skmpj.cn
http://YmJWUMKy.skmpj.cn
http://8kht7v2t.skmpj.cn
http://rd52L7fZ.skmpj.cn
http://V72FArrm.skmpj.cn
http://1kTaljhB.skmpj.cn
http://Opz3obu9.skmpj.cn
http://oRn4hoYw.skmpj.cn
http://LKw0KDOZ.skmpj.cn
http://QBrUvUHk.skmpj.cn
http://www.dtcms.com/a/368921.html

相关文章:

  • 机器学习周报十二
  • 力扣:2322. 从树中删除边的最小分数
  • 人工智能常见分类
  • C++ 音视频开发常见面试题及答案汇总
  • C/C++ Linux系统编程:线程控制详解,从线程创建到线程终止
  • swoole 中 Coroutine\WaitGroup 和channel区别和使用场景
  • HDFS架构核心
  • Python的语音配音软件,使用edge-tts进行文本转语音,支持多种声音选择和语速调节
  • 每周资讯 | 中国游戏市场将在2025年突破500亿美元;《恋与深空》收入突破50亿元
  • 别再手工缝合API了!开源LLMOps神器LMForge,让你像搭积木一样玩转AI智能体!
  • 问卷系统项目自动化测试
  • 事务管理的选择:为何 @Transactional 并非万能,TransactionTemplate 更值得信赖
  • React Fiber 风格任务调度库
  • Sentinel和Cluster,到底该怎么选?
  • 紧固卓越,智选固万基——五金及紧固件一站式采购新典范
  • android 四大组件—Activity源码详解
  • B树,B+树,B*树(无代码)
  • Redis到底什么,该怎么用
  • mysql中null值对in子查询的影响
  • 时间轮算法在workerman心跳检测中的实战应用
  • 不同行业视角下的数据分析
  • 探索Go语言接口的精妙世界
  • 如何在没有权限的服务器上下载NCCL
  • 常见Bash脚本漏洞分析与防御
  • 【算法笔记】异或运算
  • 数据结构:排序
  • mac清除浏览器缓存,超实用的3款热门浏览器清除缓存教程
  • 残差连接与归一化结合应用
  • 【知识点讲解】模型扩展法则(Scaling Law)与计算最优模型全面解析:从入门到前沿
  • MySQL锁篇-锁类型