【插头DP】BZOJ1814-Formula
【题目大意】
给出一个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的更多相关文章
- 【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板子题,刷板 ...
- bzoj1814 Ural 1519 Formula 1(插头dp模板题)
1814: Ural 1519 Formula 1 Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 924 Solved: 351[Submit][Sta ...
- bzoj1814 Ural 1519 Formula 1(插头DP)
对插头DP的理解还不是很透彻. 先说一下肤浅的理解吧. 插头DP使用范围:指数级复杂度,且适用于解决网格图连通性问题,如哈密顿回路等问题.插头一般指每相邻2个网格的接口. 题目难度:一般不可做. 使用 ...
- 插头DP讲解+[BZOJ1814]:Ural 1519 Formula 1(插头DP)
1.什么是插头$DP$? 插头$DP$是$CDQ$大佬在$2008$年的论文中提出的,是基于状压$D$P的一种更高级的$DP$多用于处理联通问题(路径问题,简单回路问题,多回路问题,广义回路问题,生成 ...
- 【Ural】1519. Formula 1 插头DP
[题目]1519. Formula 1 [题意]给定n*m个方格图,有一些障碍格,求非障碍格的哈密顿回路数量.n,m<=12. [算法]插头DP [题解]<基于连通性状态压缩的动态规划问题 ...
- RUAL1519 Formula 1 【插头DP】
RUAL1519 Formula 1 Background Regardless of the fact, that Vologda could not get rights to hold the ...
- URAL 1519 Formula 1(插头DP,入门题)
Description Background Regardless of the fact, that Vologda could not get rights to hold the Winter ...
- URAL1519 Formula 1 —— 插头DP
题目链接:https://vjudge.net/problem/URAL-1519 1519. Formula 1 Time limit: 1.0 secondMemory limit: 64 MB ...
- ural 1519 Formula 1(插头dp)
1519. Formula 1 @ Timus Online Judge 干了一天啊!!!插头DP入门. 代码如下: #include <cstdio> #include <cstr ...
随机推荐
- 如何实用便捷的在本地真机调试WEB端HTML5网页
先简单介绍两款常用但需要一定条件或限制的工具 1.如果你能FQ chrome在32版本后就自带了移动端调度工具,可以在Android直接联调,但唯一遗憾的是,在我大天朝要FQ后才能行的通,我自己试了后 ...
- tf.segment_sum和tf.unsorted_segment_sum理解实例
本文来自 guotong1988 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/guotong1988/article/details/77622790 import ...
- AUC画图与计算
利用sklearn画AUC曲线 from sklearn.metrics import roc_curve labels=[1,1,0,0,1] preds=[0.8,0.7,0.3,0.6,0.5] ...
- Thinkphp的SQL查询方式
一.普通查询方式 a.字符串$arr=$m->where("sex=0 and username='gege'")->find();b.数组$data['sex']=0 ...
- makefile使用.lds链接脚本以及 $@ ,$^, $,< 解析【转】
转自:http://www.cnblogs.com/lifexy/p/7089873.html 先来分析一个简单的.lds链接脚本 例1,假如现在有head.c init.c nand.c main. ...
- Ubuntu 17.10 安装 “爱壁纸” 时,缺失了 python-support 依赖
Ubuntu 17.10 安装 "爱壁纸" 的 deb 包时,缺失了 python-support 依赖.使用 sudo apt-get -f install 也没修复.查了下官 ...
- 4、GitLab 创建、删除、修改项目
一.gitLab创建项目 1.创建用户组 2.填写组信息后单击“Create group” 其中:“Group path”将显示在git路径中 3.选择需要加入该组的“用户”和“角色”后点击“Add ...
- set -o vi AIX下shell
set -o vi 再用esc+K键就可以使用上一条指令了 esc+kesc+j上下翻 ksh默认是emacs风格的.set -o emacs 在AIX下使用自己已经使用过的命令 在AIX下使用,默认 ...
- Python+Selenium 自动化实现实例-数据驱动实例
#coding=utf-8 from selenium import webdriver driver = webdriver.Firefox() driver.implicitly_wait(10) ...
- Spring学习-理解IOC和依赖注入
最近刚买了一本介绍ssm框架的书,里面主要对Mybatis.spring.springmvc和redis做了很多的讲解,个人觉得虽然有的内容我看不懂,但是整体上还是不错的.最近正在学习中,一边学习一边 ...