首页 > 其他分享 >【学习笔记】插头 DP

【学习笔记】插头 DP

时间:2023-07-13 19:26:04浏览次数:44  
标签:插头 tmp int 笔记 括号 方格 now DP

插头 DP,是一类解决网格图上连通性问题的状压 DP。

相关概念

轮廓线:已经决策的方格和未决策方格之间的分界线。

插头:用来描述连通性,一个方格与其某一方向的相邻方格连通,则称这个方格有某个方向的插头。容易发现在轮廓线上,每个时刻都是有 \(n\) 个上插头与 \(1\) 个左插头。

如图,红线部分为 \((3,3)\) 决策前后的轮廓线。

括号表示法

对于连通性问题,需要将当前的插头的状态进行压缩用于转移。

在哈密顿回路问题中,插头一定两两配对,且不存在相交关系,因此可以用括号表示法,将当前路径的左端插头设为 ( 并用 \(1\) 表示,右端插头设为 ) 并用 \(2\) 表示,不含插头的位置则用 0 表示。

在哈密顿路径问题中,插头可能独立存在,将这样路径设为 () 并用 \(3\) 表示。

由于合法的括号序列个数小于 \(4^{m+1}\),因此可以使用手写哈希表来减少空间,转移之和上一个方格相关,因此类似滚动数组的开两个哈希表即可。

状态转移

模板题 为例。

注意到左插头和上插头(如果存在)转移后分别变成了上插头和左插头(如果存在),且在括号序列的位置不变,转移时只需要考虑当前的两个插头即可。

设当前决策方格的左插头为 \(p_1\),上插头为 \(p_2\)。

特判当前方格存在障碍的情况,此时 \(p_1,p_2\) 必须均为 \(0\),转移后同样均为 \(0\)。

以下转移均以当前方格以及与当前方格存在插头的方格均不存在障碍为前提。

  • 若 \(p_1,p_2\) 均为 \(0\),转移相当于新建一个连通块,当前方格需要向下向右都连通,转移后 \(p_1=1,p_2=2\)。
  • 若 \(p_1\) 不为 \(0\),\(p_2\) 为 \(0\),转移相当于拓展一个连通块,向下或向右有两种情况。
  • 若 \(p_1\) 为 \(0\),\(p_2\) 不为 \(0\),同理也有两种情况。
  • 若 \(p_1=1,p_2=1\),转移相当于合并两个连通块,此时 \(p_2\) 对应的右括号变成左括号。
  • 若 \(p_1=2,p_2=2\),同理,此时 \(p_1\) 对应的左括号变成右括号。
  • 若 \(p_1=2,p_2=1\),二者直接合并。
  • 若 \(p_1=1,p_2=2\),回路闭合。

当切换一行时,注意到左插头从最后一位移至第一位,需要改变状态。

点击查看代码
int n,m;
char mp[15][15];
int ex,ey;
int head[mod+10],nxt[maxn];
int st[2][maxn],cnt[2];
ll dp[2][maxn],ans;
inline void update(int now,int s,ll val){
    int p=s%mod+1;
    for(int i=head[p];i;i=nxt[i]){
        if(st[now][i]==s) return dp[now][i]+=val,void();
    }
    st[now][++cnt[now]]=s,dp[now][cnt[now]]=val;
    nxt[cnt[now]]=head[p],head[p]=cnt[now];
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i){
        scanf("%s",mp[i]+1);
        for(int j=1;j<=m;++j){
            if(mp[i][j]=='.') ex=i,ey=j;
        }
    }
    int now=0;
    update(now,0,1);
    for(int i=1;i<=n;++i){
        for(int k=1;k<=cnt[now];++k) st[now][k]<<=2;
        for(int j=1;j<=m;++j){
            memset(head,0,sizeof(head));
            now^=1;
            cnt[now]=0;
            for(int k=1;k<=cnt[now^1];++k){
                int s=st[now^1][k];
                ll val=dp[now^1][k];
                int p1=(s>>2*(j-1))&3,p2=(s>>2*j)&3;
                int t=s-(p1<<2*(j-1))-(p2<<2*j);
                if(mp[i][j]=='*'){
                    if(!p1&&!p2) update(now,s,val);
                }
                else{
                    if(!p1&&!p2){
                        if(mp[i+1][j]=='.'&&mp[i][j+1]=='.') update(now,t+(1<<2*(j-1))+(2<<2*j),val);
                    }
                    else if(p1&&!p2){
                        if(mp[i+1][j]=='.') update(now,s,val);
                        if(mp[i][j+1]=='.') update(now,t+(p1<<2*j),val);
                    }
                    else if(!p1&&p2){
                        if(mp[i+1][j]=='.') update(now,t+(p2<<2*(j-1)),val);
                        if(mp[i][j+1]=='.') update(now,s,val);
                    }
                    else{
                        if(p1==1&&p2==1){
                            int tmp=1;
                            for(int l=j+1;l<=m;++l){
                                if((s>>(2*l)&3)==1) ++tmp;
                                if((s>>(2*l)&3)==2) --tmp;
                                if(!tmp){
                                    update(now,t-(1<<2*l),val);
                                    break;
                                }
                            }
                        }
                        else if(p1==2&&p2==2){
                            int tmp=1;
                            for(int l=j-2;l>=0;--l){
                                if((s>>(2*l)&3)==2) ++tmp;
                                if((s>>(2*l)&3)==1) --tmp;
                                if(!tmp){
                                    update(now,t+(1<<2*l),val);
                                    break;
                                }
                            }
                        }
                        else if(p1==2&&p2==1){
                            update(now,t,val);
                        }
                        else{
                            if(i==ex&&j==ey) ans+=val;
                        }
                    }
                }
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}

参考资料

标签:插头,tmp,int,笔记,括号,方格,now,DP
From: https://www.cnblogs.com/SoyTony/p/Learning_Notes_about_Plug_DP.html

相关文章

  • wireshark学习笔记
    参考博客1、https://www.cnblogs.com/yuanyuzhou/p/16308963.html功能说明使用方法1、打开wireshark之后,首先需要选择抓包的网卡,上面有显示每个网卡的流量,不知道是哪张网卡的话,可以试试多访问几次,看看流量变化情况选择。2、假如本机ip为:10.169.62.89,查看各个网卡的ip,发现是:以......
  • harbor学习笔记
    linux环境离线安装1、版本:harbor-offline-installer-v2.8.0-rc1.tgz2、一次成功的流程(仅针对于上面的版本):前置条件:需要预先安装docker、docker-compose[root@HN01harbor]#dockerversionClient:Version:18.09.0EulerVersion:18.09.0.100APIversion......
  • 动态规划DP入门笔记
    动态规划以斐波那契数列为例:\(f_i\)状态\(f_i=f_{i-1}+f_{i-2}\)转移方程\(f_0=0\),\(f_1=1\)初始化dp的实现方法一般有三种,其中的两种(最重要的)如下#include<bits/stdc++.h>usingnamespacestd;intf[200010];signedmain(){ intn; scanf("%d",&n);......
  • 数据结构练习笔记——单链表的创建
    单链表的创建【问题描述】从键盘终端输入若干整数,为其创建带头节点的单链表存储结构【样例输入】51223323345【样例输出】1223323345【样例说明】第一行的数为单链表中元素的个数,后面为各元素的值#include<iostream>usingnamespacestd;structLNode{......
  • StarRocks Segment源码阅读笔记--SegmentIterator创建
    StarRocks中要读取Segment中的数据,需要先创建SegmentIteratorStatusOr<ChunkIteratorPtr>Segment::_new_iterator(constSchema&schema,constSegmentReadOptions&read_options){DCHECK(read_options.stats!=nullptr);//tryingtoprunethecurrentse......
  • 虚树 学习笔记
    虚树学习笔记引入我们在解决树上问题时,往往都是对整棵树进行处理,或者每次询问都对一个点、点对进行处理,这类题型一般都可以通过dp、树剖解决;然而,有一类问题要求我们每次对树上一些关键点进行处理。这类问题的特点就是询问次数多,而询问的点的总数不多。可如果我们每次都把整棵......
  • office学习笔记
    目录Excel函数使用VLOOKUP制作四象限图wordPowerPointExcel函数使用VLOOKUP使用说明:https://support.microsoft.com/zh-cn/office/vlookup-函数-0bbc8083-26fe-4963-8ab8-93a18ad188a1功能:需要在表格或区域中按行查找内容时,请使用VLOOKUP。说明:在这一最简单的形式中,VLOOK......
  • vim学习笔记
    指导文档https://cloud.tencent.com/developer/beta/article/2096379常用的命令1、行号::setnu#缩写,显示行号:setnumber#全写,显示行号:setnonu#缩写,取消显示行号:setnonumber#全写,取消显示行号2、删除:dd #删除一行echo"">x......
  • shell脚本学习笔记
    目录执行一个shell脚本变量赋值引用高级变量交互式shell数值计算test命令中括号判断符默认变量$0~$n$(())、$()、``、${}、''、""、()、(())、[]、[[]]、{}条件判断-与或非函数循环标准输入输出整数比较&字符串比较shell脚本中调用另一个shell脚本的三种方式:fork、exec、......
  • psql学习笔记
    目录Q:命令行执行文件里面的语句Q:docker本地运行psqlQ:常用命令Q:windows启动命令Q:统计数据库或表的磁盘空间占用Q:查询psql中的表结构信息Q:命令行执行文件里面的语句psql-Ugalax-W"wei***@123"-ddb_name-p5432-fxxx.sqlQ:docker本地运行psql1、获取最新的postg......