【模板】插头dp
题解:
插头$dp$中经典的回路问题。
首先了解一下插头。
一个格子,上下左右四条边对应四个插头。就像这样:

四个插头。
一个完整的哈密顿回路,经过的格子一定用且仅用了两个插头。
所以所有被回路经过的格子有六种状态,即左上,左右,左下,上右,上下,右下。
这几个就是插头$dp$的基本。
然后我们来了解一下轮廓线。

红线就叫轮廓线。
我们可以利用轮廓线作为状态dp,将轮廓线一点点右推+下推,直到推完,这样我们就可以得出全局答案啦!!!
但是怎么转移……
插头!
我们可以稍微讨论一下,讨论拐角处的插头状态,然后转移就好了。
听起来很简单恶心。
实际上很简单恶心。
现在我们突然想到一个问题,就是状态怎么记录。
主要有两种方法,一种叫最小表示法(不是字符串的最小表示法),一种叫括号序列。
最小表示法,是将互相联通的插头归入一类。如果我们将其采用字典序最小的方法表示,那么对于某条轮廓线表示法与轮廓线状态一一对应。
括号表示法,是由于网格中两条哈密顿回路路径不可相交的性质。
如果我们认为回路有方向,比如轮廓线左面的为进,右面的为出,那我们可以将进看作‘(’,将出看作‘)’。
由于上面那条性质,我们可以知道一个括号序列对应一种轮廓线状态。
而且括号表示法比最小表示法好写。
括号表示法怎么用?
三进制。0表示'-',1表示'(',2表示')'。
压成一个数字然后用挂链存起来就好了。
(我用的括号表示法)
现在我们就差转移了。
(其实我非常不愿意在博客里写但是良心的我还是写了)
状态1:)(
直接用下面这个接上即可。

而且刚好满足括号匹配。
状态2:(-或)-
两种情况。
或
状态3:-)或-(
还是两种情况。
或
状态4:))或((
这个我们还是要放状态1的那个块。
但是不满足括号匹配怎么办?
向左/向右找一个换上。
举个例子,比如说原序列是:(()(()))()(),
然后中间两个接在一起,序列就应该成为:(()()--)()()
注意那个变号。
状态5:--
直接放插头。

状态6:()
一旦合并说明括号序列清空。
所以只能在最后一格合并状态6。
所以我们要知道最后一格在哪。
没有状态7。
上述状态都是在当前格子可填且插头指向格子可填时可选。
然后上代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 15
#define ll long long
#define M 100000
int n,m;
ll bas[N];
char ch[N][N];
struct Map
{
int hed[M+],cnt[];
struct EG
{
int nxt;ll to,w;
}e[<<][];
void ae(int f,ll t,ll w,int k)
{
e[++cnt[k]][k].to = t;
e[cnt[k]][k].nxt = hed[f];
e[cnt[k]][k].w = w;
hed[f] = cnt[k];
}
void push(ll u,ll d,int k)
{
int f = (int)(u%M);
for(int j=hed[f];j;j=e[j][k].nxt)
if(e[j][k].to==u)
{
e[j][k].w+=d;
return ;
}
ae(u%M,u,d,k);
}
void clear(int k)
{
memset(hed,,sizeof(hed));
cnt[k] = ;
}
}mp;
ll ans;int tx,ty;
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
{
scanf("%s",ch[i]+);
for(int j=;j<=m;j++)
if(ch[i][j]=='.')
tx=i,ty=j;
}
bas[] = ;
for(int i=;i<=m+;i++)bas[i]=bas[i-]<<;
mp.push(,,);
for(int k=,i=;i<=n;i++)
{
for(int j=;j<=mp.cnt[k];j++)mp.e[j][k].to<<=;
for(int j=;j<=m;j++)
{
k^=;mp.clear(k);
for(int o=;o<=mp.cnt[!k];o++)
{
ll now = mp.e[o][!k].to,val = mp.e[o][!k].w;
int lp = (now>>(j-)*)&,rp = (now>>j*)&;
if(ch[i][j]=='*')
{
if(!lp&&!rp)
{
mp.push(now,val,k);
}
}else
{
if(!lp&&!rp)
{
if(ch[i+][j]=='.'&&ch[i][j+]=='.')
{
ll tmp = now+bas[j-]+bas[j]*;
mp.push(tmp,val,k);
}
}else if(!lp&&rp)
{
if(ch[i+][j]=='.')
{
ll tmp = now+bas[j-]*rp-bas[j]*rp;
mp.push(tmp,val,k);
}
if(ch[i][j+]=='.')
{
ll tmp = now;
mp.push(tmp,val,k);
}
}else if(lp&&!rp)
{
if(ch[i+][j]=='.')
{
ll tmp = now;
mp.push(tmp,val,k);
}
if(ch[i][j+]=='.')
{
ll tmp = now-bas[j-]*lp+bas[j]*lp;
mp.push(tmp,val,k);
}
}else
{
if(lp==&&rp==)
{
ll tmp = now-bas[j-]-bas[j];
int sum = ;
for(int j0=j+;j0<=m+;j0++)
{
if(((now>>(j0-)*)&)==)sum++;
if(((now>>(j0-)*)&)==)sum--;
if(!sum)
{
mp.push(tmp-bas[j0-],val,k);
break;
}
}
}else if(lp==&&rp==)
{
ll tmp = now-bas[j-]*-bas[j]*;
int sum = ;
for(int j0=j-;j0>=;j0--)
{
if(((now>>(j0-)*)&)==)sum--;
if(((now>>(j0-)*)&)==)sum++;
if(!sum)
{
mp.push(tmp+bas[j0-],val,k);
break;
}
}
}else
{
if(lp==&&rp==)
{
ll tmp = now-bas[j-]*-bas[j];
mp.push(tmp,val,k);
}else if(i==tx&&j==ty)ans+=val;
}
}
}
}
}
}
printf("%lld\n",ans);
return ;
}
【模板】插头dp的更多相关文章
- 模板—插头dp(Ural 1519 Formula 1)
括号表示法: 据说比下一个要快而且灵活. #include<iostream> #include<cstring> #include<cstdio> #define ...
- 插头DP模板
/* 插头dp模板 抄的GNAQ 的 括号表示法 */ #include<cstdio> #include<algorithm> #include<cstring> ...
- 模板:插头dp
前言: 严格来讲有关dp的都不应该叫做模板,因为dp太活了,但是一是为了整理插头dp的知识,二是插头dp有良好的套路性,所以姑且还叫做模板吧. 这里先推荐一波CDQ的论文和这篇博客http://www ...
- bzoj1814 Ural 1519 Formula 1(插头dp模板题)
1814: Ural 1519 Formula 1 Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 924 Solved: 351[Submit][Sta ...
- LG5056 【模板】插头dp
题意 题目背景 ural 1519 陈丹琦<基于连通性状态压缩的动态规划问题>中的例题 题目描述 给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路.问有多少种铺法? 输 ...
- P5056 【模板】插头dp
\(\color{#0066ff}{ 题目描述 }\) 给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路.问有多少种铺法? \(\color{#0066ff}{输入格式}\) 第1 ...
- 插头dp
插头dp 感受: 我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案.而是方案本来就在那里,我们只是枚举状态统计了答案. 看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单 ...
- hdu1964之插头DP求最优值
Pipes Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Subm ...
- 插头dp初探
问题描述 插头dp用于解决一类可基于图连通性递推的问题.用插头来表示轮廓线上的连通性,然后根据连通性与下一位结合讨论进行转移. 表示连通性的方法 与字符串循环最小表示不同,这种方法用于给轮廓线上的联通 ...
随机推荐
- HDU6069:Counting Divisors(因子数统计|区间筛)
题意 计算\(\sum_{i=l}^kd(i^k)(d_i代表i的因子数)\) 分析 比赛搞了3个小时都没搞出来,有两个思维上的trick 1.要先遍历素数,再遍历[L,R],而不是枚举每个数,然后对 ...
- hdoj2795【未完待续】
题意: 给你一个矩形h*w(h,w都是1e9),然后给你n个询问,每个询问带一个1*wi矩形,问你这个给定矩形能放在第几行(从1-h下来).如果最终放不下了,就输出-1. 比如案例: 3 5 5 2 ...
- (四)SpringBoot如何定义消息转换器
一:添加fastjson依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastj ...
- Centos 搭建Mysql-Proxy 读写分离
Mysql 读写分离 主:192.168.153.130 从:193.168.153.131 Mysql-Proxy:193.168.153.132 这里省略mysql主从同步,如果有需要,请查看:M ...
- C++构造函数与析构函数的解析
创建一个对象时,常常需要作某些初始化的工作,例如对数据成员赋初值. 注意,类的数据成员是不能在声明类时初始化的.如果一个类中所有的成员都是公用的,则可以在定义对象时对数据成员进行初始化.如: clas ...
- 洛谷1083(差分+二分 or 线段树)
第一种方法:可以二分最大天数订单的答案然后通过差分求一下是否可行. ; int n, m, a[maxn], ans; struct section { int cnt, l, r; }b[maxn] ...
- 模拟 HDOJ 5095 Linearization of the kernel functions in SVM
题目传送门 /* 题意:表达式转换 模拟:题目不难,也好理解题意,就是有坑!具体的看测试样例... */ #include <cstdio> #include <algorithm& ...
- 学习JavaScript数据结构与算法 (二)
学习JavaScript数据结构与算法 的笔记 包含第四章队列, 第五章链表 本人所有文章首发在博客园: http://www.cnblogs.com/zhangrunhao/ 04队列 实现基本队列 ...
- 482 License Key Formatting 注册码格式化
详见:https://leetcode.com/problems/license-key-formatting/description/ C++: class Solution { public: s ...
- spring-bean(三)
配置方式:通过工厂方法配置bean,通过FactoryBean配置bean 配置形式:基于注解的方式 组件扫描 泛型依赖注入 静态工厂方法 /** * 静态工厂方法:直接调用某一个类的静态方法就可以返 ...