题目描述

题解:

插头$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的更多相关文章

  1. 模板—插头dp(Ural 1519 Formula 1)

    括号表示法: 据说比下一个要快而且灵活. #include<iostream> #include<cstring> #include<cstdio> #define ...

  2. 插头DP模板

    /* 插头dp模板 抄的GNAQ 的 括号表示法 */ #include<cstdio> #include<algorithm> #include<cstring> ...

  3. 模板:插头dp

    前言: 严格来讲有关dp的都不应该叫做模板,因为dp太活了,但是一是为了整理插头dp的知识,二是插头dp有良好的套路性,所以姑且还叫做模板吧. 这里先推荐一波CDQ的论文和这篇博客http://www ...

  4. bzoj1814 Ural 1519 Formula 1(插头dp模板题)

    1814: Ural 1519 Formula 1 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 924  Solved: 351[Submit][Sta ...

  5. LG5056 【模板】插头dp

    题意 题目背景 ural 1519 陈丹琦<基于连通性状态压缩的动态规划问题>中的例题 题目描述 给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路.问有多少种铺法? 输 ...

  6. P5056 【模板】插头dp

    \(\color{#0066ff}{ 题目描述 }\) 给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路.问有多少种铺法? \(\color{#0066ff}{输入格式}\) 第1 ...

  7. 插头dp

    插头dp 感受: 我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案.而是方案本来就在那里,我们只是枚举状态统计了答案. 看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单 ...

  8. hdu1964之插头DP求最优值

    Pipes Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Subm ...

  9. 插头dp初探

    问题描述 插头dp用于解决一类可基于图连通性递推的问题.用插头来表示轮廓线上的连通性,然后根据连通性与下一位结合讨论进行转移. 表示连通性的方法 与字符串循环最小表示不同,这种方法用于给轮廓线上的联通 ...

随机推荐

  1. hdoj5003【wa水】

    蜜汁wa,蜜汁wa,少了个\n------ #include<bits/stdc++.h> using namespace std; typedef long long LL; typed ...

  2. python property的2种使用方法

    一.property类 class Person(): def __init__(self, name): self.set_name(name) def get_name(self): return ...

  3. P5072 [Ynoi2015]盼君勿忘

    传送门 一开始理解错题意了--还以为是两个子序列相同的话只算一次--结果是子序列里相同的元素只算一次-- 对于一个区间\([l,r]\),设其中\(x\)出现了\(k\)次,那么它的贡献就是它的权值乘 ...

  4. 黑客攻防技术宝典web实战篇:攻击应用程序架构习题

    猫宁!!! 参考链接:http://www.ituring.com.cn/book/885 随书答案. 1. 假设受攻击的应用程序使用两台不同的服务器:一台应用程序服务器和一台数据库服务器.已经发现一 ...

  5. 51NOD 1134 最长上升子序列

    给出长度为N的数组,找出这个数组的最长递增子序列.(递增子序列是指,子序列的元素是递增的) 例如:5 1 6 8 2 4 5 10,最长递增子序列是1 2 4 5 10.   输入 第1行:1个数N, ...

  6. C++11 多线程相关的头文件

    C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是<atomic> ,<thread>,<mutex>,<condition_variable& ...

  7. Caffe实战三(依赖包解析及环境配置)

    前面的文章使用的软件环境是开始时通过apt-get命令所安装的,本文将通过编译源码的方式重新配置一个可迁移的软件环境.(参考:<深度学习 21天实战Caffe> 第五天 Caffe依赖包解 ...

  8. Codeforces 1144F(二分染色)

    发现奇环不可行,偶环可行,考虑二分图.然后染色,方向全都从一种指向另一种就可以了,随意. ; int n, m, color[maxn]; vector<int> vc[maxn]; ve ...

  9. PHP常见的输出语句 echo()、print()、print_r()、var_dump()、pinrtf()之间的区别

    echo().print().print_r().var_dump().pinrtf()之间的区别 1.print() :简单输出语句,可以输出字符串,变量.表达式等单项的值!(不能输出数组.对象等复 ...

  10. java 环境变量的设置,备忘

    新建系统变量JAVA_HOME 和CLASSPATH 变量名:JAVA_HOME 变量值:C:\Program Files\Java\jdk1.7.0变量名:CLASSPATH 变量值:.;%JAVA ...