2022年状态压缩 .pdf
《2022年状态压缩 .pdf》由会员分享,可在线阅读,更多相关《2022年状态压缩 .pdf(12页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、状态压缩郑州 101 中学 /天津大学周伟第 1 页共 12 页状态压缩Abstract 信息学发展势头迅猛,信息学奥赛的题目来源遍及各行各业,经常有一些在实际应用中很有价值的问题被引入信息学并得到有效解决。然而有一些问题却被认为很可能不存在有效的(多项式级的 )算法,本文以对几个例题的剖析,简述状态压缩思想及其应用。Keywords 状态压缩、集合、 Hash、NPC Content Introduction 作为 OIers,我们不同程度地知道各式各样的算法。这些算法有的以O(logn)的复杂度运行, 如二分查找、 欧几里德 GCD 算法(连续两次迭代后的余数至多为原数的一半 )、平衡树,
2、有的以O(n )运行,例如二级索引、块状链表,再往上有 O(n)、O(nplogqn), 大部分问题的算法都有一个多项式级别的时间复杂度上界1,我们一般称这类问题2为 P 类(deterministic Polynomial-time)问题,例如在有向图中求最短路径。 然而存在几类问题, 至今仍未被很好地解决, 人们怀疑他们根 本 没 有 多 项 式 时 间 复 杂 度 的 算 法 , 它 们 是NPC(NP-Complete) 和NPH(NP-Hard) 类,例如问一个图是否存在哈密顿圈(NPC)、问一个图是否 不存在哈密顿圈 (NPH)、 求一个完全图中最短的哈密顿圈(即经典的 Trave
3、ling Salesman Problem 货郎担问题, NPH)、在有向图中求最长 (简单)路径(NPH),对这些问题尚 不 知 有 多 项 式 时间 的 算 法 存 在 。P 和 NPC 都 是 NP(Non-deterministic Polynomial-time)的子集, NPC 则代表了 NP 类中最难的一类问题,所有的NP 类问题都可以在多项式时间内归约到NPC 问题中去。 NPH 包含了 NPC 和其他一些不属于 NP(也更难 )的问题 (即 NPC 是 NP 与 NPH 的交集 ), NPC 问题的最优化版本一般是 NPH 的,例如问一个图是否存在哈密顿圈是NPC 的,但求最
4、短的哈密顿圈则是 NPH 的,原因在于我们可以在多项式时间内验证一个回路是否真的是哈密顿回路,却无法在多项式时间内验证其是否是最短的,NP 类要求能在多项式时间内验证问题的一个解是否真的是一个解,所以最优化TSP 问题不是NP 的,而是 NPH 的。存在判定性TSP 问题,它要求判定给定的完全图是否存在权和小于某常数v 的哈密顿圈,这个问题的解显然可以在多项式时间内验证,1请注意,大O 符号表示上界,即O(n)的算法可以被认为是O(n2)的,O(nplogqn)可以被认为是O(np+1)的。2在更正式的定义中,下面提到的概念都只对判定性问题或问题的判定版本才存在。Levin 给出了一个适用于非
5、判定问题的更一般的概念,但他的论文比Cook 的晚发表 2 年。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 12 页 - - - - - - - - - 状态压缩郑州 101 中学 /天津大学周伟第 2 页共 12 页因此它是 NP 的,更精确地说是NPC 的。1如上所述,对于 NPC 和 NPH 问题,至今尚未找到多项式时间复杂度的算法。然而它们的应用又是如此的广泛, 我们不得不努力寻找好的解决方案。 毫无疑问,对于这些问题, 使用暴力的搜索是可以得到正确的答案的,
6、但在信息学竞赛那有限的时间内,很难写出速度可以忍受的暴力搜索。例如对于TSP 问题,暴力搜索的复杂度是 O(n!), 如此高的复杂度使得它对于高于10 的数据规模就无能为力了。那么,有没有一种算法,它可以在很短的时间内实现,而其最坏情况下的表现比搜索好呢?答案是肯定的状态压缩 (States Compression ,SC)。作为对下文的准备,这里先为使用Pascal的 OIers简要介绍一下 C/C+样式的位运算 (bitwise operation)。一、基本运算符名称C/C+样式Pascal样式简记法则按位与(bitwise AND) & and 全一则一否则为零按位或(bitwise
7、OR) | or 有一则一否则为零按位取反(bitwise NOT) not 是零则一是一则零按位异或(bitwise XOR) xor 不同则一相同则零以上各运算符的优先级从高到低依次为:,&,|二、特殊应用a)and:i.用以取出一个数的某些二进制位ii.取出一个数的最后一个1(lowbit)2:x&-x b)or :用以将一个数的某些位设为1 c)not:用以间接构造一些数: 0=4294967295=232-1 d)xor:i.不使用中间变量交换两个数:a=ab;b=ab;a=ab; ii.将一个数的某些位取反有了这些基础,就可以开始了。1不应该混淆P、NP、NPC、NPH 的概念。这
8、里只是粗略介绍,详见关于算法分析的书籍,这会使新手读者的理论水平上一个台阶。弄不明白也没关系,基本不影响对本文其他部分的理解_ 2具有同样作用的还有(x-1)&xx,这个的道理更容易理解。lowbit 在树状数组 (某种数据结构 )中可以用到,这里不再单独介绍,感兴趣的可以参阅各牛的论文,或者加我QQ。建议掌握,否则可能会看不懂我的部分代码。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 12 页 - - - - - - - - - 状态压缩郑州 101 中学 /天津大学
9、周伟第 3 页共 12 页Getting Started 我们暂时避开状态压缩的定义,先来看一个小小的例题。【引例】1在 n*n(n20)的方格棋盘上放置n 个车(可以攻击所在行、列 ),求使它们不能互相攻击的方案总数。【分析】这个题目之所以是作为引例而不是例题,是因为它实在是个非常简单的组合学问题:我们一行一行放置,则第一行有n 种选择,第二行n-1,, ,最后一行只有 1 种选择,根据乘法原理, 答案就是 n!。这里既然以它作为状态压缩的引例,当然不会是为了介绍组合数学。我们下面来看另外一种解法:状态压缩递推(States Compressing Recursion,SCR)。我们仍然一行
10、一行放置。取棋子的放置情况作为状态,某一列如果已经放置棋子则为 1,否则为 0。这样,一个状态就可以用一个最多20 位的二进制数2表示。例如 n=5,第 1、3、4列已经放置,则这个状态可以表示为01101(从右到左 )。设 fs为达到状态 s的方案数,则可以尝试建立f 的递推关系。考虑 n=5,s=01101 。这个状态是怎么得到的呢?因为我们是一行一行放置的,所以当达到 s 时已经放到了第三行。 又因为一行能且仅能放置一个车,所以我们知道状态 s 一定来自:前两行在第3、4 列放置了棋子 (不考虑顺序,下同 ),第三行在第1 列放置;前两行在第 1、4 列放置了棋子,第三行在第3 列放置;
11、前两行在第 1、3 列放置了棋子,第三行在第4 列放置。这三种情况互不相交,且只可能有这三种情况。根据加法原理,fs应该等于这三种情况的和。写成递推式就是:f01101=f01100+f01001+f00101 根据上面的讨论思路推广之,得到引例的解决办法:f0=1 fs=fs 2i 其中 s0 , 01,1,11 ,s 的右起第 i+1 位为 1。3反思这个算法, 其正确性毋庸置疑 ( 可以和 n! 对比验证 ) 。但是算法的时间复杂度为 O (n2n),空间复杂度 O(2n),是个指数级的算法,比循环计算n! 差了好多,它有什么优势?较大的推广空间。4Sample Problems【例 1
12、】5在 n*n(n20)的方格棋盘上放置n 个车,某些格子不能放,求使它们不能互1本文所有例题的C+代码均可以在附件中找到。2方便、整齐起见,本文中不在数的后面标明进制。3考虑上节介绍的位运算的特殊应用,可以更精巧地实现。4还有一个很 的用处,即对新手说: “来看看我这个计算n!的程序,连这都看不懂就别OI 了”5题目来源:经典组合学问题。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 12 页 - - - - - - - - - 状态压缩郑州 101 中学 /天津大学周
13、伟第 4 页共 12 页相攻击的方案总数。【分析】对于这个题目,如果组合数学学得不够扎实,你是否还能一眼看出解法?应该很难。对于这个题目,确实存在数学方法(容斥原理 ),但因为和引例同样的理由,这里不再赘述。联系引例的思路,发现我们并不需要对算法进行太大的改变。引例的算法是在枚举当前行 (即 s 中 1 的个数,设为 r)的放置位置 (即枚举每个 1),而对于例 1,第 r 行可能存在无法放置的格子, 怎么解决这个问题呢?枚举1 的时候判断一下嘛!事实的确是这样,枚举1 的时候判断一下是否是不允许放置的格子即可。但是对于 n=20,O(n2n)的复杂度已经不允许我们再进行多余的判断。所以实现这
14、个算法时应该应用一些技巧。对于第r 行,我们用 ar表示不允许放置的情况,如果某一位不允许放置则为1,否则为 0,这可以在读入数据阶段完成。运算时,对于状态 s,用 tmps=sar来代替 s进行枚举,即不枚举s 中的 1 转而枚举 tmps 中的 1。因为 tmps 保证了无法放置的位为0,这样就可以不用多余的判断来实现算法,代码中只增加了计算a数组和 r 的部分,而时间复杂度没有太大变化。这样,我们直接套用引例的算法就使得看上去更难的例1 得到了解决。你可能会说,这题用容斥原理更快。没错,的确是这样。但是,容斥原理在这题上只有当棋盘为正方形、放入的棋子个数为n、且棋盘上禁止放置的格子较少时
15、才有简单的形式和较快的速度。如果再对例1 进行推广,要在m*n 的棋盘上放置k个车,那么容斥原理是无能为力的,而SCR 算法只要进行很少的改变就可以解决问题1。这也体现出了引例中给出的算法具有很大的扩展潜力。棋盘模型是状态压缩最好的展示舞台之一。下面再看几个和棋盘有关的题目。【例 2】2给出一个 n*m 的棋盘 (n、m 80,n*m80),要在棋盘上放 k(k20)个棋子,使得任意两个棋子不相邻。 每次试验随机分配一种方案, 求第一次出现合法方案时试验的期望次数,答案用既约分数表示。【分析】显然,本题中的期望次数应该为出现合法方案的概率的倒数,则问题转化为求出现合法方案的概率。 而概率=方案
16、总数合法方案数,方案总数显然为C(n*m,k) ,则问题转化为求合法方案数。整理一下,现在的问题是:在n*m 的棋盘上放 k 个棋子,求使得任意两个棋子不相邻的放置方案数。这个题目的状态压缩模型是比较隐蔽的。观察题目给出的规模,n、m 80,这个规模要想用 SC 是困难的,若同样用上例的状态表示方法(放则为 1,不放为0),280无论在时间还是在空间上都无法承受。然而我们还看到n*m80,这种给出数据规模的方法是不多见的, 有什么玄机呢?能把状态数控制在可以承受的1如果这样改编,则状态的维数要增加,如有疑问可以参考下面的几个例子,这里不再赘述。2题目来源:经典问题改编。名师资料总结 - - -
17、精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 12 页 - - - - - - - - - 状态压缩郑州 101 中学 /天津大学周伟第 5 页共 12 页范围吗?稍微一思考,我们可以发现:9*9=8180,即如果 n,m 都大于等于 9,将不再满足 n*m80 这一条件。所以,我们有n 或 m 小于等于 8,而 28是可以承受的。我们假设mn(否则交换,由对称性知结果不变)n 是行数 m 是列数,则每行的状态可以用m 位的二进制数表示。但是本题和例1 又有不同:例 1 每行每列都只能放置一个
18、棋子, 而本题却只限制每行每列的棋子不相邻。但是,上例中枚举当前行的放置方案的做法依然可行。我们用数组s1.num1保存一行中所有的 num 个放置方案,则 s 数组可以在预处理过程中用DFS 求出,同时用 ci保存第 i 个状态中 1 的个数以避免重复计算。开始设计状态。如注释一所说,维数需要增加,原因在于并不是每一行只放一个棋子, 也不是每一行都要求有棋子,原先的表示方法已经无法完整表达一个状态。我们用fijk 表示第 i 行的状态为 sj且前 i 行已经放置了 k 个棋子2的方案数。沿用枚举当前行方案的做法,只要当前行的方案和上一行的方案不冲突即可,“微观”地讲,即ssnumi 和ssn
19、umi-1 没有同为 1 的位,其中 snumx表示第 x 行的状态的编号。然而,虽然我们枚举了第i 行的放置方案,但却不知道其上一行(i-1)的方案。为了解决这个问题,我们不得不连第i-1 的状态一起枚举,则可以写出递推式:f010=1; fijk=fi-1pk-cj 其中 s1=0 ,即在当前行不放置棋子; j 和 p 是需要枚举的两个状态编号,且要求 sj与 sp 不冲突,即 sj&sp=0。3当然,实现上仍有少许优化空间, 例如第 i 行只和第 i-1行有关,可以用滚动数组节省空间。有了合法方案数, 剩下的问题就不是很困难了, 需要注意的就只有 C(n*m,k)可能超出 64 位整数范
20、围的问题,这可以通过边计算边用GCD 约分来解决,具体可以参考附件中的代码。这个算法时间复杂度O(n*pn*num2) ,空间复杂度 ( 滚动数组)O(pn*num),对于题目给定的规模是可以很快出解的。通过上文的例题,读者应该已经对状态压缩有了一些感性的认识。下面这个题目可以作为练习。【例 3】4在 n*n(n10)的棋盘上放 k 个国王 (可攻击相邻的 8 个格子 ),求使它们无法互相攻击的方案数。【分析】其实有了前面几个例子的分析,这个题目应该是可以独立解决的。不过既然确实有疑问,那我们就来分析一下。1运用简单的组合数学知识可以求出:在格数为m 的一行上放棋子且相邻两个棋子中间的空格不能
21、少于d的num=gm+d+1,其中 gi=1 (i=1.d+1); gj=gj-d-1+gj-1 (jd)。对于本题,num=144 。此式在下文也有应用。2为了避免歧义,下文中用pn 代替原题中的k。3请读者停下来仔细揣摩这个递推式,否则可能影响对本文后面内容的理解!4题目来源: SGU.223Little Kings 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 12 页 - - - - - - - - - 状态压缩郑州 101 中学 /天津大学周伟第 6 页共 1
22、2 页首先,你应该能想到将一行的状态DFS 出来(如果不能,请返回重新阅读,谢谢), 仍然设为 s1.num,同时仍然设有数组c1.num记录状态对应的 1 的个数。和例 2 相同,仍然以 fijk 表示第 i 行状态为 sj,且前 i 行已经放置了 k 个棋子的方案数。递推式仍然可以写作:f010=1; fijk=fi-1pk-cj 其中仍要求 sj和 sp 不冲突。可是问题出来了:这题不但要求不能行、 列相邻,甚至不能对角线相邻! sj、sp 不冲突怎么“微观地”表示呢?其实,稍微思考便可以得出方法:用sp分别和 sj、sj*2、sj/2进行冲突判断即可,原理很显然。解决掉这唯一的问题,接
23、下来的工作就没有什么难度了。算法复杂度同例2。下一个例题是状态压缩棋盘模型的经典题目,希望解决这个经典的题目能够增长你的自信。【例 4】1给出一个 n*m(n100,m10)的棋盘,一些格子不能放置棋子。求最多能在棋盘上放置多少个棋子,使得每一行每一列的任两个棋子间至少有两个空格。【分析】2显然,你应该已经有DFS 搜出一行可能状态的意识了(否则请重新阅读之前的内容 3 遍,谢谢 ),依然设为 s1.num,依旧有 c1.num保存 s中 1 的个数,依照例 1 的预处理搞定不能放置棋子的格子。问题是,这个题目的状态怎么选?继续像例2、3 那样似乎不行,原因在于棋子的攻击范围加大了。但是我们照
24、葫芦画瓢:例2、3 的攻击范围只有一格,所以我们的状态中只需要有当前行的状态即可进行递推,而本题攻击范围是两格,因此增加一维来表示上一行的状态。用 fijk 表示第 i 行状态为 sj、 第 i-1行状态为 sk时前 i 行至多能放置的棋子数,则状态转移方程很容易写出:fijk=maxfi-1kl+cj 其中要求 sj,sk,sl 互不冲突。因为棋子攻击范围为两格,可以直观地想象到num 不会很大。的确,由例2中得到的 num 的计算式并代入 d=2、m=10,得到 num=60。显然算法时间复杂度为 O(n*num3),空间复杂度 (滚动数组 )O(num2)。此算法还有优化空间。我们分别枚
25、举了三行的状态, 还需要对这三个状态进行是否冲突的判断,这势必会重复枚举到一些冲突的状态组合。 我们可以在计算出s1.num后算出哪些状态可以分别作为两行的状态,这样在DP 时就不需要进行盲目的枚举。这样修改后的算法理论上比上述算法更优,但因为num 本身很小,所以这样修改没有显著地减少运行时间。值得一提的是,本题笔者的算法虽然在理论上并不是最优3,但由于位运算的使用,截至2 月 9 日,笔者的程序在PKU OJ 上长度最短,速度第二快。这个题目是国内比赛中较早出现的状态压缩题。它告诉我们状态压缩不仅可以像前几个例题那样求方案数, 而且可以求最优方案, 即状态压缩思想既可以应用到递推上 (SC
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022年状态压缩 2022 状态 压缩
限制150内