直接看CDQ在2008年的论文吧.

个人认为她的论文有两个不明确的地方, 这里补充一下: 首先是轮廓的概念. 我们在进行插头DP时, 是从上往下, 从左往右逐个格子进行的, 已经处理的格子与未经处理的格子之间的分界线叫做轮廓线. 因此每个时刻轮廓线的长度都为列数加一. 每次处理下一个格子时, 有且仅有两条轮廓线会变动.

至于什么是插头, 这个很好理解, 就是从格子里面连出来的线就叫做插头. 不难看出, 在本题中, 一个不可选的格子没有插头; 一个可选的格子有且仅有两个插头. 穿过轮廓线的插头叫做轮廓线上的插头.

剩下的自己看即可.

考虑如何DP, 根据论文的描述, 我们发现轮廓线上的插头可以被看作是一个括号序列; 一个合法的状态要求轮廓线也是合法的. 同时为了限制只能存在一条路径, 我们需要在代码中加以体现.

#include <cstdio>
#include <cstring>
#include <algorithm> using namespace std;
const int N = 15, M = 15, SZ = (int)3e5, STT = 1594323;
int n, m;
char s[N][M];
int pw[M], enc[STT], dec[SZ], tp; // 分别表示加密和解密
long long f[SZ], g[SZ];
inline int check(int stt)
{
int cnt = 0;
for(int i = 0; i < m + 1; ++ i)
{
if(stt / pw[i] % 3 == 1) ++ cnt; else if(stt / pw[i] % 3 == 2) -- cnt;
if(cnt < 0) return 0;
}
return ! cnt;
}
inline void initialize()
{
pw[0] = 1; for(int i = 1; i <= m + 1; ++ i) pw[i] = pw[i - 1] * 3;
tp = 0;
for(int i = 0; i < pw[m + 1]; ++ i) if(check(i)) enc[i] = tp, dec[tp ++] = i;
}
inline int get(int stt, int pos) {return stt / pw[pos] % 3;}
int main()
{ #ifndef ONLINE_JUDGE freopen("ural1519.in", "r", stdin);
freopen("ural1519.out", "w", stdout); #endif int lstX = -1, lstY = -1;
scanf("%d %d\n", &n, &m); for(int i = 0; i < n; ++ i) scanf("%s", s[i]);
for(int i = 0; i < n; ++ i) for(int j = 0; j < m; ++ j) if(s[i][j] == '.') lstX = i, lstY = j;
initialize();
memset(g, 0, sizeof(g)); g[enc[0]] = 1;
for(int i = 0; i < n; ++ i)
{
for(int j = 0; j < m; ++ j)
{
swap(f, g); memset(g, 0, sizeof(g));
if(s[i][j] == '*')
{
for(int k = 0; k < tp; ++ k) if(! get(dec[k], j) && ! get(dec[k], j + 1)) g[k] = f[k];
continue;
}
else for(int k = 0; k < tp; ++ k) if(f[k])
{
int x = get(dec[k], j), y = get(dec[k], j + 1);
if(! x && ! y) g[enc[dec[k] + pw[j] + pw[j + 1] * 2]] += f[k];
else if(! x ^ ! y)
{
g[k] += f[k];
g[enc[dec[k] + (y - x) * pw[j] + (x - y) * pw[j + 1]]] += f[k];
}
else if(x == 1 && y == 1)
{
int p = j + 1, cnt = 0;
for(; p <= m; ++ p)
{
if(get(dec[k], p) == 1) ++ cnt; else if(get(dec[k], p) == 2) -- cnt;
if(! cnt) break;
}
g[enc[dec[k] - pw[j] - pw[j + 1] - pw[p]]] += f[k];
}
else if(x == 2 && y == 2)
{
int p = j, cnt = 0;
for(; ~ p; -- p)
{
if(get(dec[k], p) == 2) ++ cnt; else if(get(dec[k], p) == 1) -- cnt;
if(! cnt) break;
}
g[enc[dec[k] - 2 * pw[j] - 2 * pw[j + 1] + pw[p]]] += f[k];
}
else if(x == 1 && y == 2 && i == lstX && j == lstY) g[enc[dec[k] - pw[j] - 2 * pw[j + 1]]] += f[k];
// 这里就是限制只能存在一条路径的关键
else if(x == 2 && y == 1) g[enc[dec[k] - 2 * pw[j] - pw[j + 1]]] += f[k];
}
}
swap(f, g); memset(g, 0, sizeof(g));
for(int j = 0; j < tp && dec[j] < pw[m]; ++ j) g[enc[dec[j] * 3]] += f[j];
}
printf("%lld\n", f[enc[0]]);
}

ural 1519 fomular 1 既插头DP学习笔记的更多相关文章

  1. 【BZOJ1814】Ural 1519 Formula 1 (插头dp)

    [BZOJ1814]Ural 1519 Formula 1 (插头dp) 题面 BZOJ Vjudge 题解 戳这里 上面那个链接里面写的非常好啦. 然后说几个点吧. 首先是关于为什么只需要考虑三进制 ...

  2. HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)

    插头DP基础题的样子...输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物.输出哈密顿回路(可以多回路)方案数... 看了个ppt,画了下图...感觉还是挺有效的... 参考 ...

  3. ural 1519 Formula 1(插头dp)

    1519. Formula 1 @ Timus Online Judge 干了一天啊!!!插头DP入门. 代码如下: #include <cstdio> #include <cstr ...

  4. bzoj1814 Ural 1519 Formula 1(插头DP)

    对插头DP的理解还不是很透彻. 先说一下肤浅的理解吧. 插头DP使用范围:指数级复杂度,且适用于解决网格图连通性问题,如哈密顿回路等问题.插头一般指每相邻2个网格的接口. 题目难度:一般不可做. 使用 ...

  5. URAL 1519 Formula 1(插头DP,入门题)

    Description Background Regardless of the fact, that Vologda could not get rights to hold the Winter ...

  6. URAL 1519 Formula 1 (插头DP,常规)

    题意:给一个n*m的矩阵,格子中是'*'则是障碍格子,不允许进入,其他格子都是必走的格子,所走格子形成一条哈密顿回路,问有多少种走法? 思路: 本来是很基础的题,顿时不知道进入了哪个坑.这篇插头DP的 ...

  7. 插头DP讲解+[BZOJ1814]:Ural 1519 Formula 1(插头DP)

    1.什么是插头$DP$? 插头$DP$是$CDQ$大佬在$2008$年的论文中提出的,是基于状压$D$P的一种更高级的$DP$多用于处理联通问题(路径问题,简单回路问题,多回路问题,广义回路问题,生成 ...

  8. 插头DP学习笔记——从入门到……????

    我们今天来学习插头DP??? BZOJ 2595:[Wc2008]游览计划 Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该 ...

  9. 插头DP学习笔记

    插头DP(我也不知道该怎么定义...)是一种类似于洛谷题目([模板]插头DP)的题目 题目特征为: 在棋盘上 某一维的数据范围很小 完全铺满 计数问题 直接看题吧. [模板]插头DP 给出n*m的方格 ...

随机推荐

  1. Java面试——多线程面试题总结

    )两者都在等待对方所持有但是双方都不释放的锁,这时便会一直阻塞形成死锁. //存放两个资源等待被使用 public class Resource { public static Object obj1 ...

  2. Java集合---简介

    概念 集合可以理解为一个动态的对象数组,不同的是集合中的对象内容可以任意扩充.Java最基本的集合接口:Collection接口 集合的特点 性能高 容易扩展和修改 Collection的常用子类 L ...

  3. Redis实现之AOF持久化

    AOF持久化 除了RDB持久化功能之外,Redis还提供了AOF(Append Only File)持久化功能,与RDB持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存Red ...

  4. Python虚拟机类机制之从class对象到instance对象(五)

    从class对象到instance对象 现在,我们来看看如何通过class对象,创建instance对象 demo1.py class A(object): name = "Python&q ...

  5. iOS下单例模式实现(一)(objective-c arc gcd)

    单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 这里主要介绍下在arc下,利用gcd实现单例. 第一步:声明一个静态实例 static SoundTool *_instan ...

  6. windows下使用grunt

    grunt官网:http://www.gruntjs.org/ 一.安装grunt 先安装node,在http://www.nodejs.org/可以下载安装包直接安装.在命令行下运行: npm in ...

  7. leetcode NO.53 最大子序列和 (python实现)

    来源 https://leetcode-cn.com/problems/maximum-subarray/description/ 题目描述 给出一个 32 位的有符号整数,你需要将这个整数中每位上的 ...

  8. 【bzoj4999】This Problem Is Too Simple! 树链剖分+动态开点线段树

    题目描述 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) ...

  9. 【bzoj3065】带插入区间K小值 替罪羊树套权值线段树

    题目描述 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它的随从伏特提出 ...

  10. Redis+sentinel 高可用实践

    1.环境规划 10.213.50.138(主) redis+sentinel 10.213.50.168(从) redis+sentinel 10.213.50.227  作为客户端测试插入数 2.r ...