ural 1519 fomular 1 既插头DP学习笔记
直接看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学习笔记的更多相关文章
- 【BZOJ1814】Ural 1519 Formula 1 (插头dp)
[BZOJ1814]Ural 1519 Formula 1 (插头dp) 题面 BZOJ Vjudge 题解 戳这里 上面那个链接里面写的非常好啦. 然后说几个点吧. 首先是关于为什么只需要考虑三进制 ...
- HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)
插头DP基础题的样子...输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物.输出哈密顿回路(可以多回路)方案数... 看了个ppt,画了下图...感觉还是挺有效的... 参考 ...
- ural 1519 Formula 1(插头dp)
1519. Formula 1 @ Timus Online Judge 干了一天啊!!!插头DP入门. 代码如下: #include <cstdio> #include <cstr ...
- bzoj1814 Ural 1519 Formula 1(插头DP)
对插头DP的理解还不是很透彻. 先说一下肤浅的理解吧. 插头DP使用范围:指数级复杂度,且适用于解决网格图连通性问题,如哈密顿回路等问题.插头一般指每相邻2个网格的接口. 题目难度:一般不可做. 使用 ...
- URAL 1519 Formula 1(插头DP,入门题)
Description Background Regardless of the fact, that Vologda could not get rights to hold the Winter ...
- URAL 1519 Formula 1 (插头DP,常规)
题意:给一个n*m的矩阵,格子中是'*'则是障碍格子,不允许进入,其他格子都是必走的格子,所走格子形成一条哈密顿回路,问有多少种走法? 思路: 本来是很基础的题,顿时不知道进入了哪个坑.这篇插头DP的 ...
- 插头DP讲解+[BZOJ1814]:Ural 1519 Formula 1(插头DP)
1.什么是插头$DP$? 插头$DP$是$CDQ$大佬在$2008$年的论文中提出的,是基于状压$D$P的一种更高级的$DP$多用于处理联通问题(路径问题,简单回路问题,多回路问题,广义回路问题,生成 ...
- 插头DP学习笔记——从入门到……????
我们今天来学习插头DP??? BZOJ 2595:[Wc2008]游览计划 Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该 ...
- 插头DP学习笔记
插头DP(我也不知道该怎么定义...)是一种类似于洛谷题目([模板]插头DP)的题目 题目特征为: 在棋盘上 某一维的数据范围很小 完全铺满 计数问题 直接看题吧. [模板]插头DP 给出n*m的方格 ...
随机推荐
- 1 django
1.MVC 大部分开发语言中都有MVC框架 MVC框架的核心思想是:解耦 降低各功能模块之间的耦合性,方便变更,更容易重构代码,最大程度上实现代码的重用 m表示model,主要用于对数据库层的封装 v ...
- Python中bisect的使用方法
Python中列表(list)的实现其实是一个数组,当要查找某一个元素的时候时间复杂度是O(n),使用list.index()方法,但是随着数据量的上升,list.index()的性能也逐步下降,所以 ...
- easyui-combogrid匹配查询
用到easyui-combogrid,数据比较少的情况,可以一页就显示完毕,然后直接下拉选择.但是对于数据量比较大的情况,一页显示全部显然不合适,好在从easyui-combogrid的数据加载方式可 ...
- Leetcode 576.出界的路劲数
出界的路径数 给定一个 m × n 的网格和一个球.球的起始坐标为 (i,j) ,你可以将球移到相邻的单元格内,或者往上.下.左.右四个方向上移动使球穿过网格边界.但是,你最多可以移动 N 次.找出可 ...
- html编码和解码
public static string EncodeStr(string str) { str = Regex.Replace(str, @"<html[^>]*?>.* ...
- TOJ 3974: Region n条直线m个圆最多将圆分为几个区域
3974: Region Time Limit(Common/Java):1000MS/3000MS Memory Limit:65536KByteTotal Submit: 33 ...
- c语言有用函数收集
1 strtok :分解字符串为一组字符串.s为要分解的字符串,delim为分隔符字符串.首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL. 2 strstr :从字符串str1中查找 ...
- 用 Ipe 画图
如何画一个箭头 首先要了解一个概念:「path object」. Path objects are objects that are formed with lines or curves, exam ...
- [HDU2829] Lawrence [四边形不等式优化dp]
题面: 传送门 思路: 依然是一道很明显的区间dp 我们设$dp\left[i\right]\left[j\right]$表示前$j$个节点分成了$i$块的最小花费,$w\left[i\right]\ ...
- inner join和left join 、right join 的区别?
left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录inner join(等值连接) 只 ...