【Ural】1519. Formula 1
http://acm.timus.ru/problem.aspx?space=1&num=1519
题意:给一个n×m的棋盘,其中'.'是空白,'*'是障碍,求经过所有点的哈密顿回路的数目。(n,m<=12)
#include <bits/stdc++.h>
using namespace std; typedef long long ll;
#define BIT(a,b) ((a)<<((b)<<1))
#define CLR(a,b) (a^=((a)&BIT(3,b)))
#define GET(a,b) (((a)>>((b)<<1))&3)
int n, m, lastx, lasty;
ll ans;
bool mp[12][12]; void print(int s) {
for(int i=0; i<=m; ++i) { int k=GET(s, i); if(k==0) putchar('#'); if(k==1) putchar('('); if(k==2) putchar(')'); }
puts("");
}
int find(int col, int flag, int s) {
int ret, sum=0;
if(flag==0) {
for(int i=col; i>=0; --i) {
int k=GET(s, i);
if(k==1) --sum;
if(k==2) ++sum;
if(!sum) { ret=i; break; }
}
}
else {
for(int i=col; i<=m; ++i) {
int k=GET(s, i);
if(k==1) ++sum;
if(k==2) --sum;
if(!sum) { ret=i; break; }
}
}
return ret;
}
bool getnext(int s, int row, int col, bool U, bool D, bool L, bool R, int &T, ll &sum) {
if((col==0 && L) || (col==m-1 && R)) return 0;
if((row==0 && U) || (row==n-1 && D)) return 0;
if((D && mp[row+1][col]) || (R && mp[row][col+1])) return 0;
if(row==lastx && col==lasty && (D || R)) return 0;
int l=GET(s, col), u=GET(s, col+1), d=0, r=0;
if((!l && L) || (!u && U) || (l && !L) || (u && !U)) return 0;
T=s;
// printf("s:"); print(s); printf("row:%d, col:%d, U:%d, D:%d, L:%d, R:%d", row, col, U, D, L, R);
// printf(" \t 左插头:"); if(l==0) putchar('#'); if(l==1) putchar('('); if(l==2) putchar(')');
// printf(" , 上插头:"); if(u==0) putchar('#'); if(u==1) putchar('('); if(u==2) putchar(')'); puts("");
CLR(T, col);
CLR(T, col+1);
if(!l && !u) {
if(D && R) d=1, r=2;
}
else if(l && u) {
if(l==1 && u==1) {
int pos=find(col+1, 1, s);
CLR(T, pos);
T|=BIT(1, pos);
}
else if(l==2 && u==2) {
int pos=find(col, 0, s);
CLR(T, pos);
T|=BIT(2, pos);
}
else if(l==1 && u==2) {
if(row!=lastx || col!=lasty) return 0;
ans+=sum;
}
}
else if(l && !u) {
if(D) d=l, r=0;
if(R) d=0, r=l;
}
else if(!l && u) {
if(D) d=u, r=0;
if(R) d=0, r=u;
}
T|=BIT(d, col);
T|=BIT(r, col+1); if(col==m-1) T<<=2; //printf("t:"); print(T); puts("");
return 1;
} struct H {
static const int M=1000007;
struct E { int next, to; }e[M];
int head, cnt;
int hash[M];
ll sum[M];
H() { memset(hash, -1, sizeof hash); memset(sum, 0, sizeof sum); cnt=head=0; }
bool find(int x, int &pos) {
pos=x%M;
while(1) { if(hash[pos]==x) return false; else if(hash[pos]==-1) break; ++pos; if(pos==M) pos=0; }
hash[pos]=x;
return true;
}
void ins(int t, ll d) { int pos; bool flag=find(t, pos); if(!flag) { sum[pos]+=d; return; } e[++cnt].next=head; head=cnt; e[cnt].to=pos; sum[pos]=d; }
void clr() { for(int i=head; i; i=e[i].next) hash[e[i].to]=-1, sum[e[i].to]=0; head=0; cnt=0; }
}T1, T2; #define dbg(x) cout << #x << " = " << x << endl
void bfs() {
H *q[2]; q[0]=&T1, q[1]=&T2;
q[0]->ins(0, 1);
for(int row=0; row<n; ++row) for(int col=0; col<m; ++col) {
q[1]->clr();
for(int i=q[0]->head; i; i=q[0]->e[i].next) {
ll sum=q[0]->sum[q[0]->e[i].to];
int s=q[0]->hash[q[0]->e[i].to], t;
if(mp[row][col]) {
if(getnext(s, row, col, 0, 0, 0, 0, t, sum)) q[1]->ins(t, sum);
}
else {
if(getnext(s, row, col, 1, 1, 0, 0, t, sum)) q[1]->ins(t, sum);
if(getnext(s, row, col, 1, 0, 1, 0, t, sum)) q[1]->ins(t, sum);
if(getnext(s, row, col, 1, 0, 0, 1, t, sum)) q[1]->ins(t, sum);
if(getnext(s, row, col, 0, 1, 1, 0, t, sum)) q[1]->ins(t, sum);
if(getnext(s, row, col, 0, 1, 0, 1, t, sum)) q[1]->ins(t, sum);
if(getnext(s, row, col, 0, 0, 1, 1, t, sum)) q[1]->ins(t, sum);
}
}
//printf("%d %d\n", row, col);
//for(int i=q[1]->head; i; i=q[1]->e[i].next) printf("%d ", q[1]->e[i].to); puts("");
swap(q[0], q[1]);
if(row==lastx && col==lasty) return;
}
} int main() {
scanf("%d%d", &n, &m);
for(int i=0; i<n; ++i) for(int j=0; j<m; ++j) {
char c=getchar(); while(c!='*'&&c!='.') c=getchar();
if(c=='*') mp[i][j]=1;
else lastx=i, lasty=j;
}
bfs();
printf("%lld\n", ans);
return 0;
}
我的插头dp入门题...................
关于插头dp...我简单介绍一下....(具体看cdq论文《基于连通性状态压缩的动态规划问题》ppt和word最好都看)
首先我们逐格设状态,且记录轮廓线上插头的连通情况。
在本题中,我们按照从左往右,从上到下的顺序递推,而且每一个格子有且只有两个插头,可以发现,这样我们只需要考虑轮廓线上每个格子的m个下插头和1个右插头的连通情况即可(因为这就能表示当前格子的左插头和上插头)
而插头之间的连通性我们用状压解决,原理是括号序列(当然有很多种方法,还有一种是最小表示法,听说速度很慢就没看QAQ):
首先本题要求的是回路,即路径不相交,这就提供了一个很好的性质,即性质1:
性质1:轮廓线上从左到右 4 个插头 a, b, c, d,如果 a, c 连通,并且与 b 不连通,那么 b, d 一定不连通,如图:
证明请看上边说的论文。
性质2:轮廓线上每一个连通分量恰好有 2 个插头
证明也是看论文....
然后这就能和括号序列一一对应,即每一个插头可以表示为:
0:表示没有插头,我们用'#'表示
1:表示插头是左括号,即'('
2:表示插头是有括号,即')'
因此我们对于每一个的状态,我们只需要记录:
1、轮廓线上的m个下插头和1个右插头
2、轮廓线上各个插头的连通情况(即括号序列)
而发现,状态1是可以包含在状态2内的,即当状态2中的插头对于的值是>=1的,说明就有插头。因此我们只需要记录下插头和右插头的连通情况(注意,上插头和左插头是不需要再表示了的,因为我们逐格转移的时候可以根据轮廓线上1个右插头得知左插头,一个下插头得知上插头)
而插头的连通情况我们只需要状压m+1个3进制位即可,为了效率,我们用4进制编码。(其中假如处理当前格的列是col,那么右插头的位置就是col+1)
然后我们现在来考虑状态转移(好麻烦啊...部分图片转自http://blog.sina.com.cn/s/blog_51cea4040100gmky.html):
考虑当前格子(row, col)(考虑上下左右均不是障碍且自己也不是障碍),而且在(row, col-1)(或者(row-1, m))这个轮廓线上的连通状态,我们用L表示右插头(即当前格点的左插头,位置就是col),U表示下插头(即当前格点的上插头,位置是col+1)
(在这里有个显然的技巧,我们每一次修改会修改col和col+1的状态,因此我们可以在转移时先将所有轮廓线上的连通情况直接赋值到下一个格点的轮廓线上
(在下边的讨论中,默认已经考虑了边界情况,即不费笔墨说需要边界处理的情况
1、L=0 && U=0:
此时新增了1个连通分类,如图:
此时我们只能在当前格点放下插头和右插头,即将col的状态改为1,col+1的状态改为2,也就是
## -> ()
2、L!=0 && U!=0
此时col的状态改为0, col+1的状态改为0(因为并没有下插头和右插头,也就是
## -> ##
这个需要考虑四种情况.........即L和U分别取1和2
a、L=1 && U=1
这个图大概是这样的
发现我们需要更改上插头所对应的插头的括号为2,即')',我们O(n)处理一下就能得到
b、L=2 && U=2
如图:
发现我们只需要把左插头对应的插头改为2,即')'即可..
c、L=2 && U=1
此时如图:
不需要修改....
d、L=1 && U=2
如图:
这种情况很特殊,因为如果一连上,就是一整条环了,因此只能是棋盘中最后一个可以放的格子
3、L=0 && U!=0 || L!=0 && U=0
这种情况直接判断当前要转移到的插头是下插头还是右插头即可
最后答案就是转移到2.d的方案数
怎么写呢....如果直接推....$O(nm2^{(m+1)*2})$直接跪....原因是太多无用状态....
那么我们考虑bfs拓展状态....然后hash判判重即可
这种做法复杂度还是一个迷...如何分析合法括号序列的数目.....
upd:听说和卡特兰数有关...的确...我们可以很容易得到sum{h(a)*h(n-a)}的方程....
upd:听说加了空格后和卡特兰数就没什么关系了................
【Ural】1519. Formula 1的更多相关文章
- 【Ural】1519. Formula 1 插头DP
[题目]1519. Formula 1 [题意]给定n*m个方格图,有一些障碍格,求非障碍格的哈密顿回路数量.n,m<=12. [算法]插头DP [题解]<基于连通性状态压缩的动态规划问题 ...
- 【51Nod】1519 拆方块 贪心+递推
[题目]1519 拆方块 [题意]给定n个正整数,\(A_i\)表示第i堆叠了\(A_i\)个石子.每轮操作将至少有一面裸露的石子消除,问几轮所有石子均被消除.\(n \leq 10^5\). [算法 ...
- 【Ural】【1519】Formula 1
插头DP 本题为CDQ<基于连通性状态压缩的动态规划的……(我忘了)>里的例题!(嗯就是这样……) 先膜拜一下ccy大神……http://blog.sina.com.cn/s/blog_5 ...
- 【Ural】【1057】Amount of degrees
数位DP 2009年刘聪<浅谈数位类统计问题> 例题一 从组合数 以及 数位DP的角度都可以做…… 首先转化成求1~n内K进制下只有0.1的数的个数: 考虑K进制下第一个为1的位,剩下的数 ...
- 【URAL】1960. Palindromes and Super Abilities
http://acm.timus.ru/problem.aspx?space=1&num=1960 题意:给一个串s,要求输出所有的s[0]~s[i],i<|s|的回文串数目.(|s|& ...
- 【HDOJ6217】BBP Formula(公式)
题意:给定一个无穷项的分式,它的和等于π,问π的十六进制表示的小数点后第n位是多少 1 ≤ n ≤ 100000 思路:From https://blog.csdn.net/meopass/artic ...
- 【BZOJ1814】Ural 1519 Formula 1 (插头dp)
[BZOJ1814]Ural 1519 Formula 1 (插头dp) 题面 BZOJ Vjudge 题解 戳这里 上面那个链接里面写的非常好啦. 然后说几个点吧. 首先是关于为什么只需要考虑三进制 ...
- 【BZOJ1814】Ural 1519 Formula 1 插头DP
[BZOJ1814]Ural 1519 Formula 1 题意:一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数.(n,m<=12) 题解:插头DP板子题,刷板 ...
- bzoj 1814 Ural 1519 Formula 1 插头DP
1814: Ural 1519 Formula 1 Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 942 Solved: 356[Submit][Sta ...
随机推荐
- Git常用命令总结
Git常用命令总结 git init 在本地新建一个repo,进入一个项目目录,执行git init,会初始化一个repo,并在当前文件夹下创建一个.git文件夹. git clone ...
- Android 面试题汇总
面试题基础储备 1.Activity相关 a.Activity的特点 1.可见 2.可交互 他之所以可交互,是因为他同时实现了Window.Callback和KeyEvent.Callback, 可 ...
- 转载文档:Storm实战常见问题及解决方案
该文档为实实在在的原创文档,转载请注明: http://blog.sina.com.cn/s/blog_8c243ea30101k0k1.html 类型 详细 备注 该文档是群里几个朋友在storm实 ...
- C#知识点整理
1.我们在Main()函数中,调用Test()函数,我们管Main()函数称之为调用者, 管Test()函数称之为被调用者. 如果被调用者想要得到调用者的值: 1).传递参数. 2).使用静态字段来模 ...
- sp_addlinkedserver '(null)' is an invalid product name
使用SSMS 2008客户端工具逆向生成了创建链接服务器的脚本时,在测试环境执行是报如下错误:'(null)' is an invalid product name. USE [master] GO ...
- docker核心原理
容器概念. docker是一种容器,应用沙箱机制实现虚拟化.能在一台宿主机里面独立多个虚拟环境,互不影响.在这个容器里面可以运行着我饿们的业务,输入输出.可以和宿主机交互. 使用方法. 拉取镜像 do ...
- openstack云5天资料
在网上看到有个人的博客,写了个openstack云5天学习资料.对于英文不怎么好的童鞋来说,感觉还可以.可以对openstack有所了解和认识,对后续openstack更加深入的学习有很大的帮组. ...
- 数据处理之PostgreSQL过程语言学习
前段时间,公司更换新的PostgreSQL数据集市的系统过程中,自己下载了postgresqlAPI的pdf文件研究了一下PostgreSQL数据集市.发现使用PostgreSQL过程语言可以大大加快 ...
- java 异常
1.java异常 2.自定义抛出 3.运行时异常,程序有问题,让使用者可以改' ' 4.return 和 throw的区别 return 符合函数要求的值 throw 有问题的时候用它结束 ...
- Zip加密
http://www.cnblogs.com/kgdxpr/archive/2013/08/01/3230174.html