题意:有一个n*m矩阵,其中有些格子必走,有些格子不可走,其他格子是可走也可不走,问有多少条哈密顿回路?

思路:

  本来是一道很简单的题,代码写多了连白痴bug都查不出了,竟然用i>=ex&&j>=ey来判定最后一个必走点后面的点!明显是错的。

  其实主要在选走的格子,那么有两种选择,“走”or“不走”,不走的话上一个格子的状态照搬过来。这样就没有了固定的终点了,因为终点可以是很多种情况(比如是选走点/必走点),那么只要是在ex和ey后面的格子(指的是(ex,ey)之后遍历的所有非障碍格子),都是可以作为终点的,只有在这些格子才可以将左右括号连起来(只统计但不插入)。其他的基本和常规的一样了,本次仍然用括号表示法,再过滤掉很多无效的状态,所以代码略长,但容易看。

 //#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#define pii pair<int,int>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int N=;
const int mod=;
const int NN=;
char g[N][N];
LL ans;
int cur, n, m, ex, ey, tx, ty;
struct Hash_Map
{
int head[mod]; //桶指针
int next[NN]; //记录链的信息
LL status[NN]; //状态
LL value[NN]; //状态对应的DP值。
int size; void clear() //清除哈希表中的状态
{
memset(head, -, sizeof(head));
size = ;
} void insert(LL st, LL val) //插入状态st的值为val
{
int h = st%mod;
for(int i=head[h]; ~i; i=next[i])
if(status[i] == st) //这个状态已经存在,累加进去。
{
value[i] += val;
return ;
}
status[size]= st; //找不到状态st,则插入st。
value[size] = val;
next[size] = head[h] ; //新插入的元素在队头
head[h] = size++;
}
}hashmap[]; inline int getbit(LL s,int pos) //取出状态s的第pos个插头
{
return (s>>*pos)&;
}
inline int setbit(LL s,int pos,int bit) //将状态s的第pos个插头设置为bit
{
if(s&(<<*pos )) s^=<<(*pos);
if(s&(<<(*pos+))) s^=<<(*pos+);
return (s|(bit<<*pos));
} int Fr(LL s,int pos,int bit) //寻找状态s的第pos个插头对应的右括号。
{
int cnt=;
for(pos+=; pos<m; pos++)
{
if(getbit(s, pos)==-bit) cnt++;
if(getbit(s, pos)==bit) cnt--;
if(cnt==-) return setbit(s, pos, -bit);
}
}
int Fl(LL s,int pos,int bit) //寻找状态s的第pos个插头对应的左括号。
{
int cnt=;
for(pos--; pos>=; pos--)
{
if(getbit(s, pos)==-bit) cnt++;
if(getbit(s, pos)==bit) cnt--;
if( cnt==-) return setbit(s, pos, -bit);
}
} void DP(int i,int j) //非障碍空格
{
LL t;
for(int k=; k<hashmap[cur^].size; k++)
{
LL s=hashmap[cur^].status[k];
LL v=hashmap[cur^].value[k];
int R=getbit(s,j), D=getbit(s,j+);
if(g[i][j]=='X') //障碍格子
{
if( R== && D== ) hashmap[cur].insert(s, v);
continue ;
}
if(R && D) //两个括号
{
t=(setbit(s,j,)&setbit(s,j+,));
if(R==D) //同个方向的括号
{
if(R==) t=Fr(t, j, ); //要改
else t=Fl(t, j, );
hashmap[cur].insert(t, v);
}
else if( R== && D== ) //不同的连通分量
hashmap[cur].insert(t, v);
else if(t== && ( i>ex || (i==ex && j>=ey))) //注意点!
ans+=v;
}
else if(R || D) //仅1个括号
{
if(R)
{
if(i+<n && g[i+][j]!='X') hashmap[cur].insert(s, v);
if(j+<m && g[i][j+]!='x') hashmap[cur].insert(setbit(setbit(s,j,), j+, R), v);
}
else
{
if(j+<m && g[i][j+]!='X') hashmap[cur].insert(s, v);
if(i+<n && g[i+][j]!='X') hashmap[cur].insert(setbit(setbit(s,j+,), j, D), v);
}
}
else //无括号
{
if( g[i][j+]!='X' && g[i+][j]!='X' && j+<m && i+<n ) //新括号
hashmap[cur].insert( setbit(s,j,)|setbit(s,j+,), v);
if( g[i][j]=='*' ) //此点可不走
hashmap[cur].insert( s, v);
}
}
} void cal()
{
for(int i=; i<n; i++)
{
cur^=;
hashmap[cur].clear();
for(int j=; j<hashmap[cur^].size; j++) //新行,需要左移一下状态。
hashmap[cur].insert( hashmap[cur^].status[j]<<, hashmap[cur^].value[j] );
for(int j=; j<m; j++)
{
cur^=;
hashmap[cur].clear();
DP(i,j);
if(i==tx && j==ty) return ; //最后的有效点
}
}
} int main()
{
//freopen("input.txt", "r", stdin);
int t, Case=;
cin>>t;
while(t--)
{
tx=ty=ex=ey=-;
ans=cur=;
memset(g, 'X', sizeof(g));
scanf("%d%d",&n,&m);
for(int i=; i<n; i++) //输入矩阵
{
for(int j=; j<m; j++)
{
char c=getchar();
if(c=='X'||c=='*'||c=='O')
{
g[i][j]=c;
if( c=='O' ) ex=i,ey=j; //必走格
if( c!='X' ) tx=i,ty=j; //终点格
}
else j--;
}
} hashmap[cur].clear();
hashmap[cur].insert(, ); //初始状态
cal();
cout<<"Case "<<++Case<<": "<<ans<<endl;
}
return ;
}

AC代码

FZU 1977 Pandora adventure (插头DP,常规)的更多相关文章

  1. FZU1977 Pandora adventure —— 插头DP

    题目链接:https://vjudge.net/problem/FZU-1977  Problem 1977 Pandora adventure Accept: 597    Submit: 2199 ...

  2. FZU 1977 Pandora adventure (DP)

    题意:给定一个图,X表示不能走,O表示必须要走,*表示可走可不走,问你多少种走的法,使得形成一个回路. 析: 代码如下: #pragma comment(linker, "/STACK:10 ...

  3. 【FZU】1977 Pandora adventure

    http://acm.fzu.edu.cn/problem.php?pid=1977 题意:n×m的网格,有3种格子,'O'必须经过.'*'可以选择经过.'X'不能经过.现在要求路径经过所有'O'且是 ...

  4. HDU 3377 Plan (插头DP,变形)

    题意:有一个n*m的矩阵,每个格子中有一个值(可能负值),要从左上角走到右下角,求路径的最大花费. 思路: 除了起点和终点外,其他的点可以走,也可以不走. (2)我用的是括号表示法,所以起始状态为') ...

  5. 插头DP专题

    建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议 ...

  6. 插头dp练习

    最近学了插头dp,准备陆续更新插头dp类练习. 学习论文还是cdq那篇<基于连通性状态压缩的动态规划问题>. 基本的想法都讲得很通透了,接下来就靠自己yy了. 还有感谢kuangbin大大 ...

  7. URAL 1519 Formula 1 (插头DP,常规)

    题意:给一个n*m的矩阵,格子中是'*'则是障碍格子,不允许进入,其他格子都是必走的格子,所走格子形成一条哈密顿回路,问有多少种走法? 思路: 本来是很基础的题,顿时不知道进入了哪个坑.这篇插头DP的 ...

  8. 插头dp

    插头dp 感受: 我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案.而是方案本来就在那里,我们只是枚举状态统计了答案. 看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单 ...

  9. HDU 4113 Construct the Great Wall(插头dp)

    好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm- ...

随机推荐

  1. 深入Mybatis配置文件

    Configuration是干嘛的 Configuration就像是Mybatis的总管,Mybatis的所有配置信息都存放在这里,此外,它还提供了设置这些配置信息的方法.Configuration可 ...

  2. mosquitto.conf之log配置

    # ================================================================= # Logging # 日志信息 # ============= ...

  3. TCPflow:在Linux中分析和调试网络流量的利器(转)

    TCPflow是一款功能强大的.基于命令行的免费开源工具,用于在Unix之类的系统(如Linux)上分析网络流量.它可捕获通过TCP连接接收或传输的数据,并存储在文件中供以后分析,采用的格式便于协议分 ...

  4. TypeScript完全解读(26课时)_5.TypeScript完全解读-函数

    5.TypeScript完全解读-函数 新建function.ts.然后在index.ts内引用 给函数定义参数类型:上面是es5的写法 下面是ts6的写法 一个完整的函数类型.括号 箭头 numbe ...

  5. 项目中常用的js骚操作

    //打开网址window.open("http://www.runoob.com"); //判断是否为url var url = $("#url").val() ...

  6. 算法学习--Day6

    题目描述 实现一个加法器,使其能够输出a+b的值. 输入描述: 输入包括两个数a和b,其中a和b的位数不超过1000位. 输出描述: 可能有多组测试数据,对于每组数据, 输出a+b的值. 示例1 输入 ...

  7. Android近场通信---NFC基础(一)(转)

    转自 http://blog.csdn.net/think_soft/article/details/8169483 本文译自:http://developer.android.com/guide/t ...

  8. javascript的学习笔记---复习及学习

    1.javascript包含三大部分(BOM,DOM,ECMAscript) ECMAscript:规定js的语法规范 BOM:Document Object Model 给我们提供了一套完整的操作页 ...

  9. 用css固定textarea文本域大小尺寸

    textarea元素在chrome等浏览器下可以被拖拉从而改变大小,对于查看textarea里面的内容来说相当方便,但是有时候 我们为了保持网页的美观,不得不想要禁掉这个功能,禁止用户随意拉动text ...

  10. localStorage和sessionStorage使用

    localStorage.setItem("key","value");//存数据 localStorage.getItem("key"); ...