【题目大意】

给出一个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. JS设计模式——2.初识接口

    什么是接口 接口提供了一种用以说明一个对象应该具有哪些方法的手段. 接口之利 1.接口具有自我描述性从而促进代码的重用 2.接口有助于稳定不同中的类之间的通信方式 3.测试和调试也变得更轻松 接口之弊 ...

  2. SVM支持向量机的基本原理

    SVM支持向量机的基本原理 对于很多分类问题,例如最简单的,一个平面上的两类不同的点,如何将它用一条直线分开?在平面上我们可能无法实现,但是如果通过某种映射,将这些点映射到其它空间(比如说球面上等), ...

  3. Linux 内核进程管理之进程ID【转】

    转自:http://www.cnblogs.com/hazir/p/linux_kernel_pid.html Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构, ...

  4. windows安装React Native开发运行环境

    React Native是什么 React Native是facebook开源的一个用于开发app的框架.React Native的设计理念:既拥有Native (原生) 的用户体验.又保留React ...

  5. input只读属性 设置和移除 选择数字

    设置只读属性 $('#stage').attr("readonly", "readonly"); 移除 只读属性  $("input").r ...

  6. udev和rules使用规则

    本文以通俗的方法阐述 udev 及相关术语的概念.udev 的配置文件和规则文件,然后以 Red Hat Enterprise Server 为平台演示一些管理设备文件和查询设备信息的实例.本文会使那 ...

  7. Python股票信息抓取(三)

    最近在看mongodb,然后会用了一些最简单的mongodb的操作,然后想着结合股票信息的数据的抓取,然后将数据存储在mongodb中,对于mongo和数据库的最大的区别是,mongo不需要建表,直接 ...

  8. 机顶盒 gettimeofday()获取毫秒溢出

    最近在写代码的时候遇见了一个bug,在获取当前时间戳的毫秒时,我自己测试的时候总是OK的,但是测试那边总是测不对,之前一直以为是因为我存储的类型的不对,从long long类型从lld改成llu,然后 ...

  9. 简易代理服务器之python实现

    代理服务器是在client和server之间的一个服务器,一般起到缓存的作用,所以也叫缓存服务器.比如: A ----(HTTP)----> B ----(HTTP)----> C 其中A ...

  10. CSU 1351 Tree Counting

    原题链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1351 DP题,毫无疑问.由于动态规划题目做得少.不熟悉,刚开始自己用f[i]表示用 i ...