HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)
插头DP基础题的样子。。。输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物。输出哈密顿回路(可以多回路)方案数。。。
看了个ppt,画了下图。。。感觉还是挺有效的。。。
参考http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710343.html
以及推荐cd琦的论文ppthttp://wenku.baidu.com/view/4fe4ac659b6648d7c1c74633.html
向中学生学习~~
感觉以后可能还会要看这篇日志。所以特意加了较多的注释。。。观客可以看注释=。=推荐画示意图。。。。
复杂度O(n*m*2^(m+1))
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <set>
using namespace std; #define ll long long
#define inf 0x3f3f3f3f
#define eps 1e-8 int a[][];
ll dp[][][<<];
int main(){
int t,ca=;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
for(int i=;i<=n;++i)for(int j=;j<=m;++j)scanf("%d",&a[i][j]);
memset(dp,,sizeof(dp));
dp[][][]=;
int ma=(<<(m+));
for(int i=;i<=n;++i){
for(int j=;j<=m;++j){
for(int k=;k<ma;++k){
int d=<<(j-),r=<<j;
if(a[i][j]){// 该位置无障碍
if( ( (k&d)&&(k&r) ) || ( (~k&d)&&(~k&r)) )// 右插下插同时有或同时无,必须翻转
dp[i][j][k]=dp[i][j-][k^d^r];
else // 右插下插只有一个,可翻转可不翻转
dp[i][j][k]=dp[i][j-][k]+dp[i][j-][k^d^r];
}
else {// 该位置有障碍
if((k&d)==&&(k&r)==)// 没有右插没有下插,则方案数跟随
dp[i][j][k]=dp[i][j-][k];
}
}
}
if(i+<=)
for(int k=;k<(<<m);++k)// 换行去掉上一行的右插,下一行的开头没有进来的左插,请画图
dp[i+][][k<<]=dp[i][m][k];
}
printf("Case %d: There are %I64d ways to eat the trees.\n",++ca,dp[n][m][]);
}
return ;
}
做了上面那个入门级别的插头DP。。。做了个还是入门级别的。。。。
输入N,M<=12,以及N*M的01矩阵,'*'('.')表示有(无)障碍物。输出哈密顿单回路方案数。。。
这一题需要用到括号匹配,最小表示法(上面那题可以不用,我的代码就是没有用到的)。。。
这一题参考了别人的代码,尝试了2种表示方法,一种是直接存括号对应的连通分量标号,一种是只存括号的类型。。。
我的第一种方法300+ms,第二种100+ms。。。感觉还是有点区别的。。。因为存连通分量标号需要用到3位(连通分量标号最大值为MAXM/2),而括号类型只需要2位
。。
而这题跟上一题的区别,就是,单哈密顿回路的括号匹配,如果左插是'(',下插是')',那只能在最后一个非障碍物的位置连接。。。。
具体看代码吧。。。还是建议画下示意图。。。
还有一点就是,由于用到HASH,所以HASH太大太小都可能会TLE
300+ms的版本(其实就是那个链接里的。。。。):
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <set>
using namespace std; #define ll long long
#define inf 0x3f3f3f3f
#define eps 1e-8 #define maxd 15
#define HASH 100007
#define STATE 1000010 int n,m;
int maze[maxd][maxd],code[maxd];
int ch[maxd];//最小表示法使用
int ex,ey;//最后一个非障碍格子的坐标
struct HASHMAP{
int head[HASH],nxt[STATE],sz;
ll state[STATE];
ll f[STATE];
void clear(){sz=;memset(head,-,sizeof(head));}
void push(ll st,ll ans){
int h=st%HASH;
for(int i=head[h];i!=-;i=nxt[i])
if(state[i]==st){
f[i]+=ans;
return;
}
state[sz]=st;
f[sz]=ans;
nxt[sz]=head[h];
head[h]=sz++;
}
}hm[];
void decode(int *code,int m,ll st){
for(int i=m;i>=;i--)code[i]=st&,st>>=;
}
ll encode(int *code,int m){//最小表示法
int cnt=;
memset(ch,-,sizeof(ch));
ch[]=;
ll st=;
for(int i=;i<=m;i++){
if(ch[code[i]]==-)ch[code[i]]=cnt++;
code[i]=ch[code[i]];
st<<=;
st|=code[i];
}
return st;
}
void shift(int *code,int m){
for(int i=m;i>;i--)code[i]=code[i-];
code[]=;
}
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;k++){
decode(code,m,hm[cur].state[k]);
int left=code[j-],up=code[j];
if(left&&up){
if(left==up){//只能出现在最后一个非障碍格子
if(i==ex&&j==ey){
code[j-]=code[j]=;
if(j==m)shift(code,m);
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
}
else{//不在同一个连通分量则合并
code[j-]=code[j]=;
for(int t=;t<=m;t++)
if(code[t]==up)code[t]=left;
if(j==m)shift(code,m);
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
}
else if(left || up){
int t = left + up;
if(maze[i][j+]){
code[j-]=;
code[j]=t;
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
if(maze[i+][j]){
code[j-]=t;
code[j]=;
if(j==m)shift(code,m);
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
}
else{//无插头,则构造新的连通块
if(maze[i][j+]&&maze[i+][j]){
code[j-]=code[j]=;
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
}
}
}
void dpblock(int i,int j,int cur){
for(int k=;k<hm[cur].sz;k++){
decode(code,m,hm[cur].state[k]);
code[j-]=code[j]=;
if(j==m)shift(code,m);
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
}
char str[maxd];
void init(){
memset(maze,,sizeof(maze));
ex=;
for(int i=;i<=n;i++){
scanf("%s",str+);
for(int j=;j<=m;j++){
if(str[j]=='.'){// 无障碍
ex=i,ey=j;
maze[i][j]=;
}
}
}
}
void solve(){
int cur=;
ll ans=;
hm[].clear();
hm[].push(,);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
hm[cur^].clear();
if(maze[i][j])dpblank(i,j,cur);
else dpblock(i,j,cur);
cur^=;
}
for(int i=;i<hm[cur].sz;i++)
ans+=hm[cur].f[i];
printf("%I64d\n",ans);
}
int main(){
while(~scanf("%d%d",&n,&m)){
init();
if(ex==)puts("");// 没有空的格子
else solve();
}
return ;
}
100+ms的版本(改编自那个链接里的。。。。):
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <set>
using namespace std; #define ll long long
#define inf 0x3f3f3f3f
#define eps 1e-8 #define maxd 15
#define HASH 100007
#define STATE 1000010 int n,m;
int maze[maxd][maxd],code[maxd];
int ch[maxd];//最小表示法使用
int ex,ey;//最后一个非障碍格子的坐标
struct HASHMAP{
int head[HASH],nxt[STATE],sz;
ll state[STATE];
ll f[STATE];
void clear(){sz=;memset(head,-,sizeof(head));}
void push(ll st,ll ans){
int h=st%HASH;
for(int i=head[h];i!=-;i=nxt[i])
if(state[i]==st){
f[i]+=ans;
return;
}
state[sz]=st;
f[sz]=ans;
nxt[sz]=head[h];
head[h]=sz++;
}
}hm[];
void decode(int *code,int m,ll st){
for(int i=;i<=m;++i)code[i]=st&,st>>=;
}
ll encode(int *code,int m){//最小表示法
ll st=;
for(int i=m;i>=;--i)
st=st<<|code[i];
return st;
}
void shift(int *code,int m){
for(int i=m;i>;i--)code[i]=code[i-];
code[]=;
}
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;k++){
decode(code,m,hm[cur].state[k]);
int left = code[j-],up = code[j];
if(left&&up){
if(left== && up==){// 只能出现在最后一个非障碍格子
if(i==ex&&j==ey){
code[j-]=code[j]=;
ll tmp = encode(code,m);
if(j==m)tmp<<=;
hm[cur^].push(tmp,hm[cur].f[k]);
}
}
else{//不在同一个连通分量则合并
if(left== && up==){// ...(...(...))... ==> ...(...)...##...
for(int jj=j-,cnt=;jj>=;--jj){
if(code[jj]==)++cnt;
else if(code[jj]==)--cnt;
if(cnt==){code[jj]=-code[jj];break;}
}
code[j-]=code[j]=;
}
else if(left== && up==){// ...((...)...)... ==> ...##...(...)...
for(int jj=j,cnt=;jj<=m;++jj){
if(code[jj]==)++cnt;
else if(code[jj]==)--cnt;
if(cnt==){code[jj]=-code[jj];break;}
}
code[j-]=code[j]=;
}
else if(left== && up==){// ...(...)(...)... ==> ...(...##...)...
code[j-]=code[j]=;
}
if(j==m)shift(code,m);
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
}
else if(left || up){
int t = left | up;
if(maze[i][j+]){
code[j-]=;
code[j]=t;
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
if(maze[i+][j]){
code[j-]=t;
code[j]=;
if(j==m)shift(code,m);
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
}
else{//无插头,则构造新的连通块
if(maze[i][j+]&&maze[i+][j]){
code[j-]=,code[j]=;
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
}
}
}
void dpblock(int i,int j,int cur){
for(int k=;k<hm[cur].sz;k++){
decode(code,m,hm[cur].state[k]);
code[j-]=code[j]=;
if(j==m)shift(code,m);
hm[cur^].push(encode(code,m),hm[cur].f[k]);
}
}
char str[maxd];
void init(){
memset(maze,,sizeof(maze));
ex=;
for(int i=;i<=n;i++){
scanf("%s",str+);
for(int j=;j<=m;j++){
if(str[j]=='.'){// 无障碍
ex=i,ey=j;
maze[i][j]=;
}
}
}
}
void solve(){
int cur=;
ll ans=;
hm[].clear();
hm[].push(,);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
hm[cur^].clear();
if(maze[i][j])dpblank(i,j,cur);
else dpblock(i,j,cur);
cur^=;
}
for(int i=;i<hm[cur].sz;i++)
ans+=hm[cur].f[i];
printf("%I64d\n",ans);
}
int main(){
while(~scanf("%d%d",&n,&m)){
init();
if(ex==)puts("");// 没有空的格子
else solve();
}
return ;
}
HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)的更多相关文章
- 【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 ...
- 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 ...
- hdu 1693 Eat the Trees——插头DP
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1693 第一道插头 DP ! 直接用二进制数表示状态即可. #include<cstdio> # ...
- HDU 1693 Eat the Trees(插头DP)
题目链接 USACO 第6章,第一题是一个插头DP,无奈啊.从头看起,看了好久的陈丹琦的论文,表示木看懂... 大体知道思路之后,还是无法实现代码.. 此题是插头DP最最简单的一个,在一个n*m的棋盘 ...
- HDU - 1693 Eat the Trees(多回路插头DP)
题目大意:要求你将全部非障碍格子都走一遍,形成回路(能够多回路),问有多少种方法 解题思路: 參考基于连通性状态压缩的动态规划问题 - 陈丹琦 下面为代码 #include<cstdio> ...
- HDU 1693 Eat the Trees(插头DP,入门题)
Problem Description Most of us know that in the game called DotA(Defense of the Ancient), Pudge is a ...
- HDU 1693 Eat the Trees (插头DP)
题意:给一个n*m的矩阵,为1时代表空格子,为0时代表障碍格子,问如果不经过障碍格子,可以画一至多个圆的话,有多少种方案?(n<12,m<12) 思路: 这题不需要用到最小表示法以及括号表 ...
- HDU 1693 Eat the Trees ——插头DP
[题目分析] 吃树. 直接插头DP,算是一道真正的入门题目. 0/1表示有没有插头 [代码] #include <cstdio> #include <cstring> #inc ...
随机推荐
- vmstat、top
vmstat是一个查看虚拟内存(Virtual Memory)使用状况的工具,使用vmstat命令可以得到关于进程.内存.内存分页.堵塞IO.traps及CPU活动的信息. vmstat 最常用的有两 ...
- 由pthread_create引起的段错误
一般线程的结束是由进程内的其他线程来结束的,调用pthread_cancel. 但是需要考虑到被结束线程的性质,一方面,线程是可被结束,也可无法结束,即不响应该信号:另一方面,如果线程是可被结束的,那 ...
- 3_mysql 主从复制
mysql 主从复制 网易数据库 石勇 提纲 什么是主从复制 主从复制的原理 主从复制的用途 主从复制的搭建 主从复制的问题 什么是主从复制 数据拷贝 准实时 源-主节点:目的-从节点 主从复制的原理 ...
- ajax基础了解
使用Ajax的最大优点,就是能在不更新整个页面的前提下维护数据.这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变过的信息.AJAX即“Asynchronous JavaSc ...
- Shanghai Regional Online Contest 1004
Shanghai Regional Online Contest 1004 In the ACM International Collegiate Programming Contest, each ...
- Bus Hound 的使用方法
背景: 最近在研究USB相关协议,需要对USB数据进行抓取分析,Bus Hound是个非常赞的工具,在此将其使用方法记录下来,以备下次快速上手使用. 正文: 主界面如下: 首先关注菜单栏三个选项: C ...
- PHP函数 rtrim() 的一个怪异现象
今天用rtrim()函数时遇到了一个奇怪的问题: echo rtrim('<p></div>', '</div>'); // 输出为 <p echo ltri ...
- C和指针 第一章 字符串处理程序
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_COL 20 #def ...
- linux 使用rpm安装软件时,遇到"warning: rpmts_HdrFromFdno: Header V3 RSA/SHA256 Signature, key ID fd431d51: NOKEY "错误
建议的做法: warning: rpmts_HdrFromFdno: Header V3 RSA/SHA256 Signature, key ID fd431d51: NOKEY 网上资料说这是 ...
- linux下用Apache一个IP多个域名建虚拟主机
如有两个域名,分别是hello.abc.com和play.abc.com,需把这两个域名都绑定到 IP是219.13.34.32的服务器上 1.首先需在域名供应商管理页面指定域名和IP的对应关系 2. ...