首页 > 其他分享 >状压 DP 学习笔记

状压 DP 学习笔记

时间:2023-12-19 11:57:56浏览次数:27  
标签:右移 sit int 状压 笔记 枚举 DP

前言

2023.8.30 开始停课集训。

开始补 \(CSP-S\) 的知识点,先打算来学状压 \(DP\)。

定义

状压 \(DP\) 的全称是状态压缩动态规划,也是动态规划中的一种。但是其与普通 \(DP\) 不同的是它将某种状态(一般为二进制 \(01\) 串,\(1\) 表示选,\(0\) 表示不选。也有其它进制)作为了 \(dp\) 数组的下标,这样更方便两种不同状态之间的转移。因为状态是用二进制表示的,所以状压 \(DP\) 的时间复杂度一般为 \(O(n \times 2^{n})\) 或者 \(O(n^{2} \times 2^{n})\)。所以状压 \(DP\) 最大能接受的数据范围是 \(n<=20\)。一般情况下看到数据范围小于 \(20\) 的时候都可以考虑状压。

位运算

状压的实现在于位运算,只有将位运算弄清楚了,才能写好状压。

  • 左移 / 右移

左移和右移的定义是在二进制表示下将一个数向左 / 右移。所以左移 \(x\) 位相当于多添 \(x\) 个 \(0\),右移 \(x\) 位相当于减少 \(x\) 位。

比如 \((11001011)_{2}\) 左移 \(3\) 位后为:\((11001011000)_{2}\),\((11001011)_{2}\) 右移 \(3\) 位后为:\((11001)_{2}\)。

可以发现,左移和右移相当于乘/除以 \(2^{x}\)。

  • 与 \(&\) / 或 \(|\) / 非 \(!\) / 异或 \(^\)

与 \(&\):两位都为 \(1\) 结果才为 \(1\)。

或 \(|\):两位至少有一位为 \(1\) 结果才为 \(1\)。

非 \(!\):非 \(1\) 则 \(0\),非 \(0\) 则 \(1\)。

异或 \(^\):两位不同,结果才为 \(1\)。

位运算常用技巧

  • 询问第 \(i\) 位是否为 \(1\):x & (1 << i - 1)

  • 询问两个数是否在同一个位置上都是 \(1\):x & y

  • 将第 \(i\) 位变成 \(1\),其余位不变:x ^ (1 << i - 1)

  • 这个数的最后一个 \(1\) 以及后面的 \(0\):x & -x

  • 询问这个数一共包含了多少个 \(1\):__builtin_popcount(x)

做法

状压 \(DP\) 很多时候需要初始化两个数组 \(sit\) 和 \(sta\)。\(sit_{i}\) 表示 \(i\) 所对应的十进制数是多少,\(sta_{i}\) 表示 \(i\) 中包含多少个 \(1\)。有了这两个数组后 \(dp\) 会变得更容易。而这个过程一般用一个 \(dfs\) 来实现。但这两个数组的定义有一个前提:\(i\) 状态一定合法。

Code

inline void Dfs(int x,int num,int cur)
{
	if(cur >= m)
	{
		sit[++cnt] = x;
		sta[cnt] = num;
		return; 
	}
	Dfs(x,num,cur + 1);
	Dfs(x + (1 << cur),num + 1,cur + 2);
} 

\(dp\) 部分一般会先枚举 \(n\)(也就是每个位置),再枚举状态。如果从状态 \(j\) 到状态 \(k\) 是合法的,那么就转移 \(dp\) 值。判断合不合法可以写在一个 \(Compatible\) 函数里,也可以直接写在循环里。

Code

inline bool Compatible(int j,int x)
{
	if(sit[j] & sit[x]) return false;
	if((sit[j] << 1) & sit[x]) return false;
	if((sit[x] << 1) & sit[j]) return false;
	return true;
}

状压 \(DP\) 还会用到枚举子集。如果直接枚举的活可能会 \(TLE\),但是我们可以对做法进行优化:

for(int j = i & (i - 1);j;j = (j - 1) & x)

这样就不会枚举到一些不合法的序列,时间复杂度从\(O(4^{n})\) 优化到了 \(O(3^{n})\),证明略。

总结

状压 \(DP\) 总体来说还是比较板的,无论是状态还是转移。而且这一类题的特点一般都很明显,一眼就可以看出来是状压,特别是数据范围。但是题型还是很多变的。

例题

【提高组——专题训练】状态压缩DP

标签:右移,sit,int,状压,笔记,枚举,DP
From: https://www.cnblogs.com/Creeperl/p/17913368.html

相关文章

  • c#学习笔记-------------------------readonly修饰符
    一、ReadOnly关键字MSDN官方的解释readonly 关键字是可以在字段上使用的修饰符。当字段声明包括 readonly 修饰符时,该声明引入的字段赋值只能作为声明的一部分出现,或者出现在同一类的构造函数中.具体意思是:readonly是一个修饰字段的关键字:被它修饰的字段只有在初始化或者......
  • rust语言_学习笔记
    rust语言_学习笔记转载注明来源:本文链接来自osnosn的博客,写于2023-12-10.安装rust【安装_rustup_cargo_rustc_交叉编译测试】cargo的config设置更换ustc源,使用代理。设置缺省registry。见【rustcargo配置】。crate库搜索去【crates.io】搜索去【docs.......
  • 网络流学习笔记
    这个必须写。先梳理一下,到时候再整理,证明先简写或者跳过。流网络:一个有向图,每条边有一个容量,有一个源点\(s\)和一个汇点\(t\)。每条边有一个属性称为容量,如果把流网络抽象成水管的话,那么边的容量就是每根水管的每秒最大承受的进水量。每条边也有一个流量,这个值大于等于\(0\)......
  • 阅读笔记《掌握需求过程》2
    这次我们从第三章开始看,项目启动有关的事项。这一章包含12小节,即icebreaker项目(就是本书中为了方便读者理解需求过程,始终贯穿的实例),产品目标——我们需要该产品的原因是什么,谁为它付钱:客户和顾客,用户——理解他们,风险承担者和顾问,需求限制条件,为您的宝宝命名,设定范围,该产品的成本......
  • 大数据实验报告 | 填坑笔记
     利用JavaAPI进行这个查找操作的时候,总是顺序输出,考虑是代码的原因 没有进行判定,所以只要不为空都输出出来了,进行条件判定指定行键之后,就可以了!redis启动不起来,考虑换个端口  input目录的创建过程遇到一些小问题 删除不掉就用完整目录删  地址对应正确,否则......
  • [学习笔记]珂朵莉树
    目录0x00:介绍1x00:思想1x01:节点保存1x02:核心操作split1x03:推平操作assign2x00:例题2x01:CF896C2x02:CF915E3x00:总结0x00介绍珂朵莉树(ChthollyTree),又称ODT(OldDriverTree),一种数据结构,但似乎暴力到不能称之为数据结构。可以很好地骗分,在随机数据下十分有效,常用于将\([l......
  • LightGCL Simple Yet Effective Graph Contrastive Learning For Recommendation论文
    Abstract目前的图对比学习方法都存在一些问题,它们要么对用户-项目交互图执行随机增强,要么依赖于基于启发式的增强技术(例如用户聚类)来生成对比视图。这些方法都不能很好的保留内在的语义结构,而且很容易受到噪声扰动的影响。所以我们提出了一个图对比学习范式LightGCL来减轻基于CL......
  • 《架构师之路:软件架构之美》阅读笔记三
    《架构师之路:软件架构之美》是一本关于软件架构的入门书籍,作者李家智从自己的实践经验出发,结合了业内一些经典的案例和经验,系统地介绍了软件架构的基本概念、原则和方法。本书主要分为三个部分:第一部分介绍了软件架构的基本概念和原则;第二部分详细介绍了一些常用的软件架构模式,如......
  • 阅读笔记
    第四章是需求规格的说明,在这章中作者提出需要用图形和其他形式化模型来说明需求。需求规格说明用客户的叙述性需求作为输入,用构造规格说明模型作为输出,这些模型分为3组,即状态模型,行为模型和状态变化模型。对象的状态由它的属性和关联的取值来决定,状态规格说明提供系统的静态视图,通......
  • [Vue] vue学习笔记(11): 自定义事件 & 全局事件总线
    组件的自定义事件通过props可以将信息传递给子组件,那么当子组件需要向上传递信息的时候呢,除了使用props传递函数类的方法,我们还可以用自定义事件通过父组件给子组件绑定一个事件someEvent//App.vue<Student@someEvent='getStudentName'/>//methodsmethods:{ getStu......