【动态规划】【状态压缩】LCP04 覆盖

作者推荐

【广度优先搜索】【网格】【割点】【 推荐】1263. 推箱子

本文涉及知识点

动态规划汇总

LCP04 覆盖

你有一块棋盘,棋盘上有一些格子已经坏掉了。你还有无穷块大小为1 * 2的多米诺骨牌,你想把这些骨牌不重叠地覆盖在完好的格子上,请找出你最多能在棋盘上放多少块骨牌?这些骨牌可以横着或者竖着放。
输入:n, m代表棋盘的大小;broken是一个b * 2的二维数组,其中每个元素代表棋盘上每一个坏掉的格子的位置。
输出:一个整数,代表最多能在棋盘上放的骨牌数。
示例 1:
输入:n = 2, m = 3, broken = [[1, 0], [1, 1]]
在这里插入图片描述

输出:2
解释:我们最多可以放两块骨牌:[[0, 0], [0, 1]]以及[[0, 2], [1, 2]]。(见下图)
示例 2:
输入:n = 3, m = 3, broken = []
输出:4
解释:下图是其中一种可行的摆放方式
在这里插入图片描述
限制:
1 <= n <= 8
1 <= m <= 8
0 <= b <= n * m

动态规划

动态规划状态数

mask &(1 << j) 表示第j列空闲,没有放置骨牌,也没有损坏。其它位置不限:损坏、骨牌、空闲皆可。
pre[mask] 表示第i行开始处理时,上一行状态为mask的最多骨牌数。
dp1[mask] 表示处理完第i行的竖放后,最多骨牌数。
dp2[mask] 表示处理完第i行的竖放横放后,最多骨牌数。

动态规划的转移方程

mask1 是上一行的状态。
mask2 = mask ^ 当前行坏掉的位置。
mask3 = mask1 & mask2
枚举所有 mask4(竖放),mask4是mask所有非0子序列。每个mask1都要枚举mask4
mask5是竖放完的状态 mask2 ^ mask4。
枚举mask5的所有子序列mask6,横放的状态,合法状态:数量必须是偶数,两两挨在一起。
时间复杂度: O(m3n)

动态规划的初始状态

pre[0]=0,其它-100。

动态规划的填表顺序

按行处理。

动态规划的返回值

pre的最大值。

代码

核心代码

template<class ELE>
void MaxSelf(ELE* seft, const ELE& other)
{*seft = max(*seft, other);
}
class CBitCounts
{
public:CBitCounts(int iMaskCount){for (int i = 0; i < iMaskCount; i++){m_vCnt.emplace_back(bitcount(i));}}template<class T>int bitcount(T x) {int countx = 0;while (x) {countx++;x &= (x - 1);}return countx;}vector<int> m_vCnt;
};class CEnumMask2
{
public:CEnumMask2(int iMaskCount):m_iMaskCount(iMaskCount){}template<class GetMask2,class On>void Enum(GetMask2 getMask2,On on ){for (int mask1 = 0; mask1 < m_iMaskCount; mask1++){const int mask2 = getMask2(mask1);for (int mask3 = mask2; mask3; mask3 = mask2 & (mask3 - 1)){on(mask1, mask2, mask3);}}}const int m_iMaskCount;
};
class Solution {
public:int domino(int n, int m, vector<vector<int>>& broken) {const int iMaskCount = 1 << m;vector<int> vCan(n, iMaskCount-1);for (const auto& v : broken){vCan[v[0]] ^= (1 << v[1]);}		CBitCounts bitCnt(iMaskCount);vector<int> vVilidH(iMaskCount,-100);vVilidH[0] = 0;for (int i = 1; i < iMaskCount; i++){int end = i & (-i);int end1 = end * 2;if (i & end1){vVilidH[i] = 1 + vVilidH[i ^ end ^ end1];}}vector<int> pre(iMaskCount, -100);pre[0] = 0;CEnumMask2 enumMask(iMaskCount);for (int r = 0; r < n; r++){vector<int> dp1(iMaskCount, -100);dp1[vCan[r]] = *std::max_element(pre.begin(), pre.end());//不竖放enumMask.Enum([&](int mask1) {return vCan[r] & mask1; }, [&](int mask1, int mask2, int mask3){MaxSelf(&dp1[vCan[r] ^ mask3], pre[mask1] + bitCnt.m_vCnt[mask3]); });vector<int> dp2 = dp1 ;//不横放enumMask.Enum([](int mask1) {return mask1; }, [&](int mask1, int mask2, int mask3){if (vVilidH[mask3] <= 0){return;}MaxSelf(&dp2[mask1 ^ mask3], dp1[mask1] + vVilidH[mask3]); });pre.swap(dp2);}return *std::max_element(pre.begin(), pre.end());}
};

测试用例


template<class T,class T2>
void Assert(const T& t1, const T2& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}}int main()
{int m,n;vector<vector<int>> broken;{Solution sln;n = 2, m = 3, broken = { {0, 0},{0, 1} };auto res = sln.domino(n, m, broken);Assert(2, res);}{Solution sln;n = 2, m = 3, broken = { {1, 0},{1, 1} };auto res = sln.domino(n, m, broken);Assert(2, res);}{Solution sln;n = 3, m = 3, broken = {  };auto res = sln.domino(n, m, broken);Assert(4, res);}{Solution sln;n = 4, m = 3, broken = { {1,0},{1,1} };auto res = sln.domino(n, m, broken);Assert(5, res);}{Solution sln;n = 3, m = 4, broken = { {2,2},{2,3} };auto res = sln.domino(n, m, broken);Assert(5, res);}}

优化

枚举所有合法状态,再枚举竖放状态。不用枚举前一行的状态。竖放的状态就是前一行状态。

template<class ELE>
void MaxSelf(ELE* seft, const ELE& other)
{*seft = max(*seft, other);
}
class CBitCounts
{
public:CBitCounts(int iMaskCount){for (int i = 0; i < iMaskCount; i++){m_vCnt.emplace_back(bitcount(i));}}template<class T>int bitcount(T x) {int countx = 0;while (x) {countx++;x &= (x - 1);}return countx;}vector<int> m_vCnt;
};class Solution {
public:int domino(int n, int m, vector<vector<int>>& broken) {const int iMaskCount = 1 << m;vector<int> vCan(n, iMaskCount-1);for (const auto& v : broken){vCan[v[0]] ^= (1 << v[1]);}		CBitCounts bitCnt(iMaskCount);vector<int> vHMax(iMaskCount);	for (int i = 1; i < iMaskCount; i++){int end = i & (-i);int end1 = end * 2;vHMax[i] = (i & end1) ? (1 + vHMax[i ^ end ^ end1]) : (vHMax[i ^ end]);}vector<int> pre(iMaskCount, -100);pre[0] = 0;for (int r = 0; r < n; r++){vector<int> dp(iMaskCount, -100);dp[vCan[r]] = pre[0];for (int mask = vCan[r]; mask; mask = (mask - 1) & vCan[r]){//当前行放置阵了骨牌的位置for (int maskH = mask; ; maskH = (maskH - 1) & mask){MaxSelf(&dp[vCan[r] ^ mask], vHMax[mask ^ maskH] + bitCnt.m_vCnt[maskH] + pre[maskH]);if (0 == maskH){break;}}}	pre.swap(dp);}return *std::max_element(pre.begin(), pre.end());}
};

2023年2月版

//通过 x &= (x-1)实现
int bitcount(unsigned x) {
int countx = 0;
while (x) {
countx++;
x &= (x - 1);
}
return countx;
}

class Solution {
public:
int domino(int R, int C, vector<vector>& broken) {
m_iMaskNum = 1 << C ;
vector vRowMask(R, m_iMaskNum - 1);
for (const auto& v : broken)
{
vRowMask[v[0]] &= ~(1 << v[1]);
}
vector pre(m_iMaskNum, -1);
pre[0] = 0;
for (int r = 0; r < R; r++)
{
vector dp(m_iMaskNum, -1);
for (int pr = 0; pr < m_iMaskNum; pr++)
{
const int& iPreNum = pre[pr];
if (-1 == iPreNum)
{
continue;
}
const int iCurRMask = vRowMask[r];
const int iMaxVMask = iCurRMask & pr;
//vMask枚举所有的竖放
for (int vMask = iMaxVMask;; vMask = iMaxVMask & (vMask - 1))
{
const int iMaxHMask = iCurRMask &(~vMask);
for (int hMask = iMaxHMask;; hMask = iMaxHMask& (hMask - 1))
{
const int iHNum = GetHNum(hMask);
if (iHNum < 0)
{
continue;
}
dp[iMaxHMask & ~hMask] = max(dp[iMaxHMask & ~hMask], iPreNum + iHNum + bitcount(vMask));
if (0 == hMask)
{
break;
}
}
if (0 == vMask)
{
break;
}
}
}
pre.swap(dp);
}
return *std::max_element(pre.begin(), pre.end());
}
int GetHNum(int iMask)
{
int iNum = 0;
while (iMask)
{
int iEndMask = (iMask&(-iMask));
int iPreMask = iEndMask << 1;
if (!(iMask & iPreMask))
{
return -1;
}
iMask -= iEndMask;
iMask -= iPreMask;
iNum++;
}
return iNum;
}
int m_iMaskNum;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/699991.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

maven3旧版本的下载地址(含新版本)

因为现有的3.8版本与IDEA不兼容&#xff0c;我需要下载3.6版本&#xff0c;但是官网的位置非常隐蔽&#xff0c;找了很多资料才看到。故记录一下。 第一步 进入网址&#xff0c;选择需要的版本 Index of /dist/maven/maven-3 第二步 选择binaries 第三步 选择zip文件下载就可…

【Java程序设计】【C00285】基于Springboot的游戏分享网站(有论文)

基于Springboot的游戏分享网站&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的游戏分享网站 本系统分为系统功能模块、管理员功能模块以及用户功能模块。 系统功能模块&#xff1a;在网站首页可以查看首页、游戏…

uniapp-提现功能(demo)

页面布局 提现页面 有一个输入框 一个提现按钮 一段提现全部的文字 首先用v-model 和data内的数据双向绑定 输入框逻辑分析 输入框的逻辑 为了符合日常输出 所以要对输入框加一些条件限制 因为是提现 所以对输入的字符做筛选,只允许出现小数点和数字 这里用正则实现的 小数点…

IDEA生成Java Doc帮助文档

使用场景 使用IDEA&#xff08;本次使用2020.3版&#xff09;将自己写的常用的工具类打成jar包&#xff0c;安装到maven本地仓库&#xff0c;最后生成对应的doc参考文档。 操作流程 方法一 选中项目 右键 show in Explor&#xff0c;如下图&#xff1a; 选中地址栏 cmd 输入…

无公网IP情况下如何远程查看本地群晖NAS存储的文件资源

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是前排提醒&#xff1a; 1. 搭建群晖虚拟机1.1 下载黑群晖文件vmvare虚拟机安装包1.2 安装VMware虚拟机&#xff1a;1.3 解压黑群晖虚拟机文件1.4 虚拟机初始化1.5 没有搜索到黑群晖的解…

Java的编程之旅27——继承

1.继承的简介 继承是面向对象编程中的一个重要概念&#xff0c;指的是一个类可以继承另一个类的属性和方法。被继承的类称为父类或基类&#xff0c;继承这个父类的类称为子类或派生类。 通过继承&#xff0c;子类可以继承父类的属性和方法&#xff0c;使得子类具有相似的行为…

掌握BeautifulSoup4:爬虫解析器的基础与实战【第91篇—BeautifulSoup4】

掌握BeautifulSoup4&#xff1a;爬虫解析器的基础与实战 网络上的信息浩如烟海&#xff0c;而爬虫技术正是帮助我们从中获取有用信息的重要工具。在爬虫过程中&#xff0c;解析HTML页面是一个关键步骤&#xff0c;而BeautifulSoup4正是一款功能强大的解析器&#xff0c;能够轻…

2024年度中国5G随身WiFi品牌排行榜

【中国品牌网中国3C质量评测中心权威榜单联合发布】 第一名&#xff1a;格行 优势&#xff1a;作为随身WiFi行业的佼佼者&#xff0c;格行凭借其15年的物联网行业经验&#xff0c;在技术研发、产品创新及客户服务上均享有盛誉。其5G随身WiFi产品网络稳定&#xff0c;客户满意度…

行为树入门:ROS2 BehaviorTree.CPP Groot2安装与简单使用(有例程)

ROS2安装与学习及其topic学习 BehaviorTree.CPP编译与安装 和 Groot2安装 注意&#xff1a;《ROS2行为树&#xff08;C行为树&#xff09;BehaviorTree.CPP完全图形化开发&#xff0c;完美支持ROS2话题通信》这个例程实现的前提需要建一个sentry_interfaces::msg::RobotHP的m…

Spring Boot 笔记 029 用户模块

1.1 用户信息需要在多个链接使用&#xff0c;所以需要用pinia持久化 1.1.1 定义store import {defineStore} from pinia import {ref} from vue const useUserInfoStore defineStore(userInfo,()>{//定义状态相关的内容const info ref({})const setInfo (newInfo)>{i…

“IT行业职业发展的黄金之路:哪些证书能为你增光添彩?“

文章目录 每日一句正能量前言1、浙大计算机程序设计能力考试证书&#xff08;PAT&#xff09;2、全国计算机等级考试证书(NCRE)3、计算机技术与软件专业资格考试证书&#xff08;软考&#xff09;4、通信专业技术人员职业水平证书5、全国计算机应用水平考试证书&#xff08;NIT…

MySQL学习Day19——索引的数据结构

一、为什么使用索引: 索引是存储引擎用于快速找到数据记录的一种数据结构&#xff0c;就好比一本教课书的目录部分&#xff0c;通过目录中找到对应文章的页码&#xff0c;便可快速定位到需要的文章。MySQL中也是一样的道理&#xff0c;进行数据査找时&#xff0c;首先查看查询…

【深蓝学院】移动机器人运动规划--第6章 模型预测控制(MPC)与运动规划--笔记

0. Outline 1. Reactive Control&#xff08;反应式控制&#xff09; 控制学中的 “Reactive Control” 通常指的是一种控制策略&#xff0c;它依赖于系统对特定事件或变化的即时反应&#xff0c;而不是按照预定的计划或策略行动。这种控制往往是基于当前的传感器输入来做出决…

Mac 上游玩QQ飞车详细教程,IPA砸壳包资源下载安装

最近经过一番折腾终于在 MAC 上成功安装运行了 QQ 飞车&#xff0c;上图&#xff1a; 最后总结下来安装过程其实蛮简单的&#xff0c;但其中有很多卡点和坑所以浪费了很多的时间&#xff0c;特意整理本篇教程供大家参考 大体思路是这样的&#xff0c;先安装 playCover,该软件可…

PostMan使用自带js库base64编码、sha256摘要、环境变量的使用

目录 1、环境变量的使用2、base64编码、sha256摘要、以及脚本的使用3、脚本代码 在请求调试接口的过程中&#xff0c;因为要使用大量相同的参数&#xff0c;使用变量的方式能很大程度上减轻接口调用的工作量 版本说明&#xff1a;Postman for Windows&#xff0c;Version&#…

【Python笔记-设计模式】桥接模式

一、说明 桥接模式是一种结构型设计模式&#xff0c; 主要用于将抽象部分与它的实现部分分离&#xff0c; 从而能在开发时分别使用&#xff0c;使系统更加灵活&#xff0c;易于扩展。 (一) 解决问题 所有 组合类的数量将以几何级数增长 抽象和实现分离&#xff1a;桥接模式可…

数据结构day4

实现创建单向循环链表、创建结点、判空、输出、头插、按位置插入、尾删、按位置删除 loop_list.c #include "loop_list.h" loop_p create_head() {loop_p L(loop_p)malloc(sizeof(loop_list));if(LNULL){printf("空间申请失败\n");return NULL;}L->le…

websocket了解下

websocket请求长啥样 GET /chat HTTP/1.1 Host: example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ Sec-WebSocket-Version: 13 啥是websocket websocket是http的一种&#xff0c;服务器可以主动向客户端推送信息&#xff0c;…

进程 2月24日学习笔记

1.进程: 程序&#xff1a;存放在外存中的一段数据组成的文件 进程&#xff1a;是一个程序动态执行的过程,包括进程的创建、进程的调度、进程的消亡 2.进程相关命令: 1.top 动态查看当前系统中的所有进程信息&#xff08;根据CPU占用率排序&#xff09; PID:唯一识…

Linux---权限管理(ACL权限、特殊位和隐藏属性)

目录 1.ACT权限 1.1什么是ACT权限 1.2ACT图解 2.操作步骤 2.1添加测试目录、用户、组&#xff0c;并将用户添加到组 2.2修改目录的所有者和所属组 2.3设定权限 2.4为临时用户分配权限 2.4.1添加临时用户 2.4.2为临时用户分配特定权限 2.4.3查看目录权限&#xff0c;注…