【题目大意】

给出一个m*n的矩阵里面有一些格子为障碍物,求经过所有非障碍格子的哈密顿回路个数。

【思路】

最典型的插头DP。分为三种情况:

(1)当前格子既没有上插头也没有左插头。

如果下边和右边都没有障碍,新建连同分量。

(2)如果只有左插头或者右插头。

延伸或者拐弯,当然也要判断有没有障碍。

(3)上插头和左插头都没有。

1. 如果两个插头不连通(编号不一样),那么将两个插头所处的连通分量合并,标记相同的连通块标号,O(n)扫描保证最小表示;
2. 如果已经连通,相当于出现了一个回路,这种情况只能出现在最后一个非障碍格子。

由于状态非常多,用hash表存储状态。

decode和encode注意一下,这里不赘述了。

【错误点】

注意一下ch要开得够大,具体见代码。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int MAXN=;
const int HASH=;
int ex,ey;
int m,n;
int maze[MAXN][MAXN];
int code[MAXN],ch[MAXN];
struct HashMap
{
vector<int> hash[HASH];//存储f和state的下标
vector<ll> f,state;//存储对应的方案数和状态
void init()
{
for (int i=;i<HASH;i++) vector<int>().swap(hash[i]);
vector<ll>().swap(f);
vector<ll>().swap(state);
}
void push(ll st,ll ans)
{
int h=st%HASH;
for (int i=;i<hash[h].size();i++)
{
int now=hash[h][i];
if (state[now]==st)//如果已经存储了当前状态,直接累加
{
f[now]+=ans;
return;
}
}
//如果没有存储过当前状态,累加
state.push_back(st);
f.push_back(ans);
hash[h].push_back(state.size()-);
}
}dp[]; void decode(ll st)
{
memset(code,,sizeof(code));
for (int i=n;i>=;i--)
{
code[i]=st&;//每三位代表一个信息
st>>=;
}
} ll encode()
//用最小表示法重新编码
{
int cnt=;
memset(ch,-,sizeof(ch));
ch[]=;
long long st=;
for (int i=;i<=n;i++)
{
if (ch[code[i]]==-) ch[code[i]]=cnt++;
code[i]=ch[code[i]];
st<<=;
st|=code[i];
}
return st;
} void shift()
{
for (int i=n;i>;i--) code[i]=code[i-];
code[]=;
} void dpblank(int i,int j,int cur)
{
for (int k=;k<dp[-cur].state.size();k++)
{
decode(dp[-cur].state[k]);
int left=code[j-];//左插头
int up=code[j];//上插头 /*如果上下插头都没有*/
if (!left && !up)
{
if (maze[i][j+] && maze[i+][j])
{
code[j-]=code[j]=MAXN-;
//这里只要随便设置一个大数即可 //【attention】这里千万不可以设置成MAXN,否则ch数组会抱★★★★★★★★ //因为encode会重新用最小表示法编码
dp[cur].push(encode(),dp[-cur].f[k]);
}
} /*只有上插头或者只有左插头*/
if ((left&&(!up))||((!left)&&up))
{ int t=left|up;
if (maze[i][j+])//右边没有障碍
{
code[j-]=;
code[j]=t;
dp[cur].push(encode(),dp[-cur].f[k]);
}
if (maze[i+][j])//下面没有障碍
{
code[j-]=t;
code[j]=;
if (j==n) shift();
dp[cur].push(encode(),dp[-cur].f[k]);
}
} /*上插头和右插头都有*/
if (left && up)
{
if (left==up)
{
if (i==ex && j==ey)
{
code[j-]=code[j]=;
if (j==n) shift();
dp[cur].push(encode(),dp[-cur].f[k]);
}
}
else
{
code[j-]=code[j]=;
for (int t=;t<=n;t++)
if (code[t]==up) code[t]=left;
if (j==n) shift();
dp[cur].push(encode(),dp[-cur].f[k]);
}
}
}
} void dpblock(int i,int j,int cur)
{
int k=;
for (int k=;k<dp[-cur].state.size();k++)
{
decode(dp[-cur].state[k]);
code[j-]=code[j]=;
if (j==n) shift();
dp[cur].push(encode(),dp[-cur].f[k]);
}
} void solve()
{
int cur=;
ll ans=;
dp[cur].init();
dp[cur].push(,);//DP数组初始化
for (int i=;i<=m;i++)
for (int j=;j<=n;j++)
{
cur^=;
dp[cur].init();
if (maze[i][j]) dpblank(i,j,cur);
else dpblock(i,j,cur); }
for (int i=;i<dp[cur].state.size();i++)
ans+=dp[cur].f[i];
printf("%lld",ans);
} void init()
{
memset(maze,,sizeof(maze));
ex=ey=;
for (int i=;i<=m;i++)
{
char str[MAXN];
scanf("%s",str);
for (int j=;j<n;j++)
{
if (str[j]=='.')
{
ex=i;
ey=j+;
maze[i][j+]=;
}
}
}
} int main()
{
while (scanf("%d%d",&m,&n)!=EOF)
{
init();
if (ex==) puts("");//如果没有一个是空格的话直接输出0
else solve();
}
return ;
}

【插头DP】BZOJ1814-Formula的更多相关文章

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

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

  2. 【BZOJ1814】Ural 1519 Formula 1 插头DP

    [BZOJ1814]Ural 1519 Formula 1 题意:一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数.(n,m<=12) 题解:插头DP板子题,刷板 ...

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

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

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

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

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

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

  6. 【Ural】1519. Formula 1 插头DP

    [题目]1519. Formula 1 [题意]给定n*m个方格图,有一些障碍格,求非障碍格的哈密顿回路数量.n,m<=12. [算法]插头DP [题解]<基于连通性状态压缩的动态规划问题 ...

  7. RUAL1519 Formula 1 【插头DP】

    RUAL1519 Formula 1 Background Regardless of the fact, that Vologda could not get rights to hold the ...

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

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

  9. URAL1519 Formula 1 —— 插头DP

    题目链接:https://vjudge.net/problem/URAL-1519 1519. Formula 1 Time limit: 1.0 secondMemory limit: 64 MB ...

  10. ural 1519 Formula 1(插头dp)

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

随机推荐

  1. python学习笔记(十一)之序列

    之前学习的列表,元组,字符串都是序列类型,有很多共同特点: 通过索引得到每一个元素,索引从0开始 通过分片的方法得到一个范围的元素的集合 很多通用的操作符(重复操作符,拼接操作符,成员关系操作符) 序 ...

  2. 解决ajax chrome禁止本地浏览时加载本地其他文件的方法

    在chrome快捷键右键--属性 “ --allow-file-access-from-files ”,前面用空格隔开.然后应用--确定.

  3. 一个python爬虫工具类

    写了一个爬虫工具类. # -*- coding: utf-8 -*- # @Time : 2018/8/7 16:29 # @Author : cxa # @File : utils.py # @So ...

  4. Mysql 中 char 、varchar 、text的区别

    首先它们的存储方式和数据的检索方式都不一样.数据的检索效率是:char > varchar > text 空间占用方面,就要具体情况具体分析了. char:存储定长数据很方便,CHAR字段 ...

  5. linux系统查找具体进程

    ps -ef | grep '查找内容' eg:ps -ef | grep '测试USB设备穿透'

  6. Vue.js——60分钟快速入门(转)

    var vm = new Vue({ el: '#app', data: { people: [{ name: 'Jack', age: 30, sex: 'Male' }, { name: 'Bil ...

  7. fedroa20 没法开启ntpd服务器

    1现象:ntpd老是没法开启,ntpd -d显示有个进程占用123端口. [root@vd13crmtb01 ~]# systemctl enable ntpd.service         //开 ...

  8. MVC开发模式与javaEE三层架构

    1.MVC开发模式 1. M:Model,模型.JavaBean        * 完成具体的业务操作,如:查询数据库,封装对象2. V:View,视图.JSP        * 展示数据3. C:C ...

  9. spring源码解析--事务篇(前篇)

    对于每一个JAVA程序员,spring应该是再熟悉不过的框架了,它的功能有多强大我就不多说了,既然他有这么强大的功能,是如何实现的呢?这个就需要从他的原理去了解,而最直接了解原理的方式莫过于源码.当然 ...

  10. Adding Completion to (interactive)

      Adding Completion to (interactive) Author: Tubo Question: Is there any way to add my own completio ...