[动态规划][状压DP][剪枝搜索] XDOJ 1420 2019西电acm校赛网络赛J题 垒箱子

题目链接

这次学校网络赛还是挺不错的,有3道题(F I J)都是值得一做的emm,J题胡乱暴力+dp拿了个一血,I题胡乱暴力也过了

当然由于我FFT/NTT&斯特林数学的不行所以F题并不会做,虽然推出来公式和dalao一样但是没法把复杂度降下去,等到学会了应该会放个F的题解

J题吧,跟UVa 437 The Tower of Babylon很像,当然只是看上去很像,UVa 437的限制是每种箱子必须叠在比他严格小的另一个上,然后箱子无限用,XDOJ 1420 是叠在长宽大于等于该箱子的另一个箱子上,但是每种箱子只能用一次。
如果箱子的数量少一点的话,其实完全可以直接完全状态压缩动态规划,但是这题数量是30,压缩成二进制的话2^30肯定会超时,所以需要一点操作。。

首先先全部暴搜,dfs(x,S)表示当前以箱子x号为最底部箱子,之前已经用过的也就是还在x更底下的箱子情况为S(S是一个十进制数,展开成01二进制串表示30个箱子是否被使用过)所能达到的最高的高度,每次都打一遍结果,很快可以发现,当取到某一个箱子之后,在某些情况下他后面的搜索路径就是固定的。

就比如说 某次搜到了箱子3,然后他之后的搜索路径就只能放箱子6和7,然后把每次搜索路径都打出来就会发现,其实之后的每次搜到3都会是走6和7,除非6和7之前已经被用过了也就是说6或7变成了3底下的箱子,这时候后面的路径会发生一些变化,因为每个箱子只能用一次。(这里本该举一组样例但我实在找不到当时画的样例了emm)

然后到这里就可以想一下是不是可以用这个特性对dfs(x,S)进行剪枝和记忆化搜索,首先把S的定义反转,让S这个二进制数表示尚未使用过的箱子”的情况,比如00101表示1号和3号箱子还没有被用过,245号箱子已经被用过了。这样的话,每次dfs的时候可以记录下来一个dp(x,TS)的记忆化结果,其中TS是S和“理论上可以放到x上的所有箱子的集合”KS相与的结果,这里后者可以n^2复杂度枚举预处理,我的代码里是存储到ky[x]的数组里。这样的话,每次dfs就是先把TS求出来,然后判断(x,TS)这个对有没有在之前被搜索过,如果有的话就直接返回结果,这里的这个结果可以用一个map<pair<int,int>, int> 来保存,如果没有出现过的话的话就直接暴搜,这样的话每种箱子作为底的每种情况只会被搜到一次,就等于提前把绝不可能走到的箱子和已经走过的路径都剪掉了。把所有的状况想象成图的话,每个箱子三种形态那就是有90个节点,每个结点出发能走的最长的路径最多30个,如果能保证每种情况只走一次其实是相当快的(主要是我并不会严格证明复杂度emmm)

继续阅读“[动态规划][状压DP][剪枝搜索] XDOJ 1420 2019西电acm校赛网络赛J题 垒箱子”

[状压DP][枚举子集] UVALive 4794 Sharing Chocolate

题目链接

简单分析易知如果Yes的话整块巧克力一定是从最初的一大块不断地分成矩形来实现的,而一个矩形分成两个小矩形的方法只有两种,横着分或者竖着分,这样的话就可以枚举断点分割,大问题转化为小问题递归解决。然后总共的小块数量最多15,很明显可以用二进制表示获得的块的状态。但是直接定义类似\[dp(r,c,S)\]这样的状态会造成\[O((x+y)xy2^n)\]的复杂度,基本上不能通过这个题(其实做了极致的优化的话时间上应该可以做到但是空间复杂度会超过2GB)。然后考虑状态的进一步化简,可以发现实际上如果确定了S,则可以通过r和c中的一个计算出另一个(或者发现根本不存在这样的数),由此只需要考虑\[dp(r,S)\]即可,其中r是原状态r,c中较小的那一个。另外,枚举分割点时间是并不优秀,因为还得检查当前集合是否支持这样的分割,不如直接枚举所有的子集分割情况,然后用这个来算出分割后的子函数的r和c。按照计算矩阵链乘的方式来做复杂度分析可以算出时间复杂度\[O(x3^n)\]。直接记忆化搜索的话,会比这个上限要小得多。
继续阅读“[状压DP][枚举子集] UVALive 4794 Sharing Chocolate”

[AC自动机][状态压缩DP][DFS]

题目链接: Password Suspects

这题让我回想起了某现场赛被某大佬支配的恐惧

感觉是dp,而且这个子串的数据量一看就是状压,然后在推转移方程的时候死活推不出来一个不交叉的。可耻的搜了题解,看到大佬们的标题:AC自动机+DP
我就

~~其实是常见的一种操作只是我太久没做过字符串的题目了~~
在思考递推方程的时候就一直在纠结前缀与后缀重合的这种情况怎么表示状态,然后忽然意识到ac自动机(更确切的说是Trie图)就是可以唯一的确定一个匹配状态的数据结构!这种题一般都是给定一些字串,然后给出一些约束条件,比如要求

  • 匹配所有字串,这种就是本题类型的,往往需要状压
  • 不能匹配任一字串,即匹配走过的路径上不能有任何一个危险节点
  • 满足某些条件的字串
  • 等等等等(我还没来得及总结那么多

继续阅读“[AC自动机][状态压缩DP][DFS]”

[DP][状压DP] UVa 1633 Dyslexic Gollum

自己想的时候一直在纠结的问题是怎么保存(第k个之前的是不是0/1)的问题,纠结了很久没有get到正点上,因为单单这样的话似乎会导致状态信息不足。。会漏掉一些情况。看了绿书题解,一看到状压顿时明白了。。。k最大只有10,这样的话只要把构造串的最后k位01情况压缩起来就可以了。另外一个问题就是这样的话会不会导致有中间产生长度超过k的回文串,答案是否定的,因为回文串本身其实有着子结构的性质,如果以某个节点为最后一位的长度k的回文串没有被构造出来,那么一定不会有更长的回文串包含这个点的。

继续阅读“[DP][状压DP] UVa 1633 Dyslexic Gollum”

[训练指南]UVa11825 状压dp 集合问题 二进制状态枚举所有集合

感觉如果早知道那个快速枚举子集的算法的话其实也就很简单。。太弱了orz

这个问题可以抽象成,将n个集合划分成尽可能多的组,满足约束每个组里的集合之和(所有的相或)都是全集,在这里注意这里说的原本的集合是对应于题中的节点的,一个节点和与他相邻的所有节点这一坨东西算一个基本集合。N<=16,很容易想到用二进制数字来表示集合,比如P[1]=011010表示1节点对应的集合里有1,3,4这三个节点。然后要预处理出不同每个原来的集合之间相或的结果用cov表 继续阅读“[训练指南]UVa11825 状压dp 集合问题 二进制状态枚举所有集合”