[省选前题目整理][URAL 1519]Formula 1(插头DP)
2015-03-31 11:28
281 查看
题目链接
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=22385题目大意
给出一个n∗mn*m大小的棋盘,棋盘上有空格也有障碍物,求经过所有非障碍格子的曼哈顿回路个数。思路
可以用插头DP解决此题。插头DP中的轮廓线SS的ff值就代表了轮廓线上的插头状态为SS,并且经过了被DP遍历过的所有格子的不完全曼哈顿回路个数
我这里定义的不完全曼哈顿回路指的是不封闭的一个回路。
DP过程就是从上到下,一行一行地进行遍历,格子(i,j)(i,j)的每个轮廓线状态都是由(i,j−1)(i,j-1)(若j=1j=1,就是(i−1,m)(i-1,m))的所有合法前驱状态转移而来。
在DP到格子(i,j)(i,j)时进行下面的分类讨论
一、(i,j)(i,j)是空格
1、移动拐角前的轮廓线,(i,j)(i,j)左边的插头和上边的插头都不为空
不管这两个插头是否相同,都将它们合并。
2、移动拐角前的轮廓线,(i,j)(i,j)左边的插头和上边的插头中有一个为空,设不为空的那个插头为tt
若(i,j)(i,j)右边的格子为空格,那么可以将轮廓线状态进行这样的转移:右移拐角后,新的左插头为移动前的tt。(相当于将联通块向右延伸成L型)
若(i,j)(i,j)下边的格子为空格,那么可以将轮廓线状态进行这样的转移:右移拐角后,新的下插头为移动前的tt。(相当于将联通块向下延伸成|型)
二、(i,j)(i,j)是障碍物
移动轮廓线拐角后,(i,j)(i,j)下面的插头和(i,j)(i,j)右边的插头全部清空。
代码
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 15 //棋盘大小 #define MOD 30007 //hash模数 #define STATUS 1000010 //状态数组的大小 using namespace std; typedef long long int LL; int map[MAXN][MAXN]; int code[MAXN]; //解码后的插头序列 int ch[MAXN]; //最小表示法使用,ch[i] int ex,ey; //最后一个非障碍格子坐标 struct HashMap { int head[MOD],next[STATUS],size; LL status[STATUS]; //每种状态的轮廓线 LL f[STATUS]; //每种状态对应的DP值f void init() { size=0; memset(head,-1,sizeof(head)); } void push(LL sta,LL ans) //将轮廓线状态为sta,DP值为ans的状态结点插入到hash表中 { int tmp=sta%MOD; for(int p=head[tmp];p!=-1;p=next[p]) if(status[p]==sta) { f[p]+=ans; return; } status[size]=sta; f[size]=ans; next[size]=head[tmp]; head[tmp]=size++; } }hm[2]; //此处用了滚动数组 void decode(int *code,int m,LL status) //将三进制数status转换为长度为m+1的轮廓线code { for(int i=m;i>=0;i--) { code[i]=status&7; status>>=3; //!!!!!! } } LL encode(int *code,int m) //将长度为m+1的轮廓线转换为三进制状态 { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; //!!!!!! LL status=0; //最终返回的状态 for(int i=0;i<=m;i++) { if(ch[code[i]]==-1) ch[code[i]]=cnt++; code[i]=ch[code[i]]; status<<=3; status|=code[i]; } return status; } void shift(int *code,int m) //轮廓线换行 { for(int i=m;i>0;i--) code[i]=code[i-1]; //!!!!! code[0]=0; } int n,m; //n*m大小的棋盘 void dpblank(int i,int j,int cur) //空格格子的DP { for(int k=0;k<hm[cur].size;k++) { decode(code,m,hm[cur].status[k]); int left=code[j-1]; int up=code[j]; //左插头和上插头 if(left&&up) //左插头和上插头均不为空 { if(left==up) //两个插头连的是同一联通块,加入(i,j)后会形成一个环 { if(i==ex&&j==ey) { code[j-1]=code[j]=0; if(j==m) shift(code,m); hm[cur^1].push(encode(code,m),hm[cur].f[k]); } } else //两个联通块连的不是同一联通块,加入当前格子(i,j)后两个联通块会连在一起 { code[j-1]=code[j]=0; for(int t=0;t<=m;t++) if(code[t]==up) code[t]=left; if(j==m) shift(code,m); hm[cur^1].push(encode(code,m),hm[cur].f[k]); } } else if((left&&(!up))||((!left)&&up)) //左插头和上插头中有一个为空 { int tmp; //tmp=左插头和上插头中不为空的那个插头 if(left) tmp=left; else tmp=up; if(map[i][j+1]) { code[j-1]=0; code[j]=tmp; hm[cur^1].push(encode(code,m),hm[cur].f[k]); } if(map[i+1][j]) { code[j-1]=tmp; code[j]=0; if(j==m) shift(code,m); hm[cur^1].push(encode(code,m),hm[cur].f[k]); } } else //左、上插头均为空,加入(i,j)后会形成新联通块 { if(map[i][j+1]&&map[i+1][j]) //(i,j)下面和右边的格子均不为空 { code[j-1]=code[j]=13; hm[cur^1].push(encode(code,m),hm[cur].f[k]); } } } } void dpblock(int i,int j,int cur) //(i,j)是障碍物的DP { for(int k=0;k<hm[cur].size;k++) { decode(code,m,hm[cur].status[k]); code[j-1]=code[j]=0; //加入(i,j)后,清空(i,j)位置下面和右边的插头 if(j==m) shift(code,m); hm[cur^1].push(encode(code,m),hm[cur].f[k]); } } void init() { char s[MAXN]; memset(map,0,sizeof(map)); ex=0,ey=0; for(int i=1;i<=n;i++) { scanf("%s",s+1); for(int j=1;j<=m;j++) { if(s[j]=='.') { ex=i,ey=j; map[i][j]=1; } } } } void solve() //DP过程 { int cur=0; LL ans=0; hm[cur].init(); hm[cur].push(0,1); //初始的DP边界状态:整个棋盘都没访问过,DP值为1 for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { hm[cur^1].init(); //!!!! if(map[i][j]) dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } for(int i=0;i<hm[cur].size;i++) ans+=hm[cur].f[i]; printf("%lld\n",ans); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); if(ex==0) //特判无解 { printf("0\n"); continue; } solve(); } return 0; }
相关文章推荐
- bzoj1814 Ural 1519 Formula 1(插头dp模板题)
- URAL 1519 Formula 1(插头DP)
- URAL 1519 Formula 1 插头DP
- [省选前题目整理][POJ 3133]Manhattan Wiring(插头DP)
- Ural1519 Formula 1 插头dp
- 【BZOJ1814】Ural 1519 Formula 1 插头DP
- URAL 1519 Formula 1 【插头DP模板题】
- [BZOJ]1814 Ural 1519 Formula 1 插头DP
- ural 1519 Formula 1(轮廓线|插头DP|括号配对)
- Ural 1519 Formula 1( 插头dp )
- ural 1519 Formula 1(插头dp)
- URAL 1519 Formula 1 dp(插头)
- [URAL1519] Formula 1 [插头dp入门]
- 【Ural1519】Formula 1 插头DP模板
- HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)
- 【URAL 1519】【插头dp模板】Formula 1
- 【bzoj1814】Ural 1519 Formula 1 插头dp
- Ural 1519 Formula --插头DP
- ural 1519 Formula 1(插头dp)
- [ural1519]Formula 1 && 插头DP