其实这所谓连通性状态压缩dp并不麻烦,有模板可循
算法流程
1. 预处理出所有有效状态,这个有效状态的定义是所有括号是配对的,即括号序列合法,并给每个状态一个唯一对应的哈希值(3进制),我用0表示无插头,1表示左插头,-1表示右插头
2. 预处理出所有转移,形如g[i,j,k],表示轮廓线的状态为i,转折点为j,第k号转移到哪个状态(编号,不记哈希值)。一般是以下几种转移(但对于每个特定的格子只有两种转移)
0 0 –> 0 0 or 1 -1 空格不管或在空格新建两个插头
0 1 –> 0 1 or 1 0 将一边的插头延续,2个方向均可
0 -1 -> 0 -1 or -1 0将一边的插头延续,2个方向均可
1 0 -> 1 0 or 0 1将一边的插头延续,2个方向均可
-1 0 -> -1 0 or 0 -1将一边的插头延续,2个方向均可
1 -1 -> 0 0 将一个连通块合并,“收圈”
-1 1 -> 0 0 合拢两条挤到一起的“线”
1 1 … -1 -1 -> 0 0 … 1 -1合拢两条挤到一起的“线”
1 1 … -1 -1 -> 1 -1 … 0 0合拢两条挤到一起的“线”
需要注意的是,找某状态对应的编号应用hash,不能裸找,裸找能慢十几倍
注:图片来自盾哥博客
3. 搞完了预处理,dp就容易多了,一般是根据题目看某些特殊的格子只能有特定的转移,讨论一下即可。这里的空间消耗一般会比较大,所以一般得写滚动数组
有了这些预处理,dp就方便多了,直接写就可以了
【POJ1739】
直接用连通性状态压缩dp求哈密尔顿路径有点麻烦,所以可以看成是求从(n,1)->(n,m)再从(n,m)->(n+1,m)->(n+1,1)->(n,1)的一条哈密尔顿环即可
具体的话相当于直接dp,ans=f[n+1,1,第二个有左括号,第m+1有右括号的状态]
travel
题意:
在N*M的方格上,求(a,b) -> (c,d)的哈密尔顿路径条数
算法:
在上面的基础上加入独立插头
用4进制表示,论文中没有说,这里补充一下,关于插头的准确定义
独立插头:往上延伸没有回到轮廓线上
左括号:往上延伸并回到轮廓线上,且对应点在这个位置的右边
右括号:与左括号相反
主要有以下几种转移
只有起点和终点允许的:
1.两边都没有插头 -> 往任一方向开一独立插头
2.只有一边有括号 –> 把这个括号消掉,并把这个括号原来对应的括号改成独立插头
3.只有一个独立插头过来 –>消掉这个独立插头
非起点终点或(n,m)允许的:
一边是括号,一边是独立插头 -> 合并这个括号和独立插头,同时把与那个括号配对的括号改成独立插头
(n,m)非起点终点时只有(n,m) 允许的:
合并两个独立插头
另外,所有点都不允许合并左右插头
所以这样就可以了
代码:
ps:加入独立插头后状态数激增。。
【画圈圈】
今年集训队的题目,大意就略了吧,ms衡阳八中OJ上有
题目主要就是要确定每个点是圈圈内部还是外部,这里其实只要统计包含它的括号对数即可,基数为内部,偶数为外部
代码:
这种题目还是挺麻烦的!另外,NOI2010那道神题暂时还没弄懂,以后再说吧