HDU 3681 Prison Break(状态压缩dp + BFS)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3681
前些天花时间看到的题目,但写出不来,弱弱的放弃了。没想到现在学弟居然写出这种代码来,大吃一惊附加敬仰之情。这里借用下他的成果,好好学习吧,骚年***
题意:给出矩阵(作为监狱)和在监狱中的一个装有电池的机器人,其中
- F为出发点,图中只有一个,且初始状态下机器人在此处电池为满电量;
- D为障碍物,不可走;
- S为空格子,机器可自由通过;
- G为充电点,只能充电一次,且一次能充满电池,经过G可作为S不充电,充电后G变为S;
- Y点为电闸,机器人可通过,且通过后变为S。
机器人通过所有Y后才能飞出监狱够逃走。在此之前他只能一个格子一个格子(向上、向下、向左、向右)地走动,而且每走一格消耗一格电池的电量,如果电池没电他就不能移动。问他的电池满电量状态下至少有几格电量才能使他逃出监狱?
该题的思路来源于它的数据,Y+G的数量小于15。所以从这里就可以考虑状态压缩DP。
电量求法采用的是二分查找。
整幅图显然要进行处理,Y,F都是必须要互相连通的,若不连通,显然是无解,反之必然有解。将Y,F,G对应的编号,起点F定位0,Y,G依次编号,将Y和G分成两块编号,那么接下来对于电闸Y和充电点G的判断就会比较容易,只要直接判断编号范围即可。
接下来就是求对于每个点的最短路了,再求完最短路后,如发现Y,F间某2点无法连通,则无解,否则有解。
若有解,则进行状态压缩DP,对于每一个maxt(当前的电池满格电量),途中大于maxt的,则不能更新,反正则可以进行判断并更新。若每个Y点都已经遍历过,则判断该状态下是否可行,可行则返回TRUE。
代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int INF=<<;
int as[];
void st()
{
int i, k=;
for(i=; i<; i*=)
{
as[i]=++k;
}
}
struct Node
{
int x, y;
} nod[];
struct POS
{
POS() {}
POS(int a,int b,int c)
{
x=a,y=b,now=c;
}
int x, y, now;
} pos;
int n, m, p, q, sx, sy;
int dis[][], dp[][], the[][], num, ll;
char map[][];
bool ans;
void init() //对每个Y,F,G编号
{
int i, j;
memset(the,-,sizeof(the));
p=;
for(i=; i<n; i++)
{
for(j=; j<m; j++)
{
if(map[i][j]=='F')
sx=i, sy=j;
if(map[i][j]=='Y')
{
nod[p].x=i, nod[p].y=j;
the[i][j]=p; //该数组可用于判断某个坐标的点的编号
p++;
}
}
}
q=p;
for(i=; i<n; i++)
for(j=; j<m; j++)
{
if(map[i][j]=='G')
{
nod[q].x=i, nod[q].y=j;
the[i][j]=q;
q++;
}
}
nod[].x=sx, nod[].y=sy;
the[sx][sy]=;
for(i=; i<q; i++)
for(j=; j<q; j++)
{
if(i==j)
dis[i][j]=;
else dis[i][j]=INF;
}
}
int dx[]= {,,-,};
int dy[]= {,,,-};
bool can(int x,int y)
{
if(x<||x>=n||y<||y>=m||map[x][y]=='D')
return false;
return true;
}
void bfs() //BFS求Y,F,G之间的最短路
{
int i, j, nx, ny, x, y, now;
POS tmp;
bool vis[][];
for(i=; i<q; i++)
{
memset(vis,,sizeof(vis));
queue<POS> qq;
qq.push(POS(nod[i].x,nod[i].y,));
while(!qq.empty())
{
tmp=qq.front();
qq.pop();
x=tmp.x, y=tmp.y, now=tmp.now;
for(j=; j<; j++)
{
nx=x+dx[j], ny=y+dy[j];
if(can(nx,ny)&&!vis[nx][ny])
{
if(the[nx][ny]!=-)
dis[i][the[nx][ny]]=now+;
vis[nx][ny]=true;
qq.push(POS(nx,ny,now+));
}
}
}
}
for(i=; i<p; i++)
for(j=; j<p; j++)
if(dis[i][j]==INF) ans=false;
}
bool deal(int maxt) //状态压缩DP判断是否可行
{
int i, j, k, tmp;
for(i=; i<num; i++)
for(j=; j<q; j++)
dp[i][j]=INF;
dp[][]=;
int ed;
for(i=; i<num; i++)
{
for(j=i; j>; j-=j&(-j))
{
tmp=j&(-j);
ed=as[tmp];
for(k=; k<q; k++)
if(dp[i^tmp][k]+dis[k][ed]<=maxt)
dp[i][ed]=min(dp[i^tmp][k]+dis[k][ed],dp[i][ed]);
if(ed>=p&&dp[i][ed]!=INF)
dp[i][ed]=;
if(dp[i][ed]!=INF)
{
for(k=; k<p; k++)
{
if(dp[i][ed]+dis[ed][k]<=maxt)
dp[i|(<<(k-))][k]=min(dp[i|(<<(k-))][k],dp[i][ed]+dis[ed][k]);
}
for(; k<q; k++)
{
if((<<(k-))&i) continue;
if(dp[i][ed]+dis[ed][k]<=maxt)
dp[i|(<<(k-))][k]=;
}
}
}
if(i%ll==ll-)
{
for(j=; j<q; j++)
if(dp[i][j]<=maxt) return true;
}
}
return false;
}
int main()
{
int i, j, tmp, l, r;
st();
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==&&m==) break;
for(i=; i<n; i++)
scanf("%s",map[i]);
init();
ans=true;
bfs();
num=<<(q-); //所有状态个数
l=, r=;
ll=<<(p-);
if(ans) //ans为true表示,Y和F间两两都是连通的
{
while(l<r) //二分求解
{
m=(l+r)>>;
if(deal(m))
r=m;
else l=m+;
}
}
if(ans) printf("%d\n",r);
else puts("-1");
}
return ;
}
HDU 3681 Prison Break(状态压缩dp + BFS)的更多相关文章
- hdu 3681 Prison Break(状态压缩+bfs)
Problem Description Rompire . Now it’s time to escape, but Micheal# needs an optimal plan and he con ...
- HDU 4511 (AC自动机+状态压缩DP)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4511 题目大意:从1走到N,中间可以选择性经过某些点,比如1->N,或1->2-> ...
- HDU 3001 Travelling(状态压缩DP+三进制)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3001 题目大意:有n个城市,m条路,每条路都有一定的花费,可以从任意城市出发,每个城市不能经过两次以上 ...
- hdu 4057(ac自动机+状态压缩dp)
题意:容易理解... 分析:题目中给的模式串的个数最多为10个,于是想到用状态压缩dp来做,它的状态范围为1-2^9,所以最大为2^10-1,那我们可以用:dp[i][j][k]表示长度为i,在tri ...
- HDU 3681 Prison Break(BFS+二分+状态压缩DP)
Problem Description Rompire is a robot kingdom and a lot of robots live there peacefully. But one da ...
- HDU 3681 Prison Break(状压DP + BFS)题解
题意:一张图,F是起点,Y是必须要到的点,D不能走,G可以充电.可以往四个方向走,每走一步花费一个电,走到G可以选择充满电或者不充,每个G只能充一次.问你走遍Y的最小初始点亮.number(G) + ...
- HDU 3681 Prison Break 越狱(状压DP,变形)
题意: 给一个n*m的矩阵,每个格子中有一个大写字母,一个机器人从‘F’出发,拾取所有的开关‘Y’时便能够越狱,但是每走一格需要花费1点能量,部分格子为充电站‘G’,每个电站只能充1次电.而且部分格子 ...
- hdu 3681 Prison Break (TSP问题)
Prison Break Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...
- hdu 2825(ac自动机+状态压缩dp)
题意:容易理解... 分析:在做这道题之前我做了hdu 4057,都是同一种类型的题,因为题中给的模式串的个数最多只能为10个,所以我们就很容易想到用状态压缩来做,但是开始的时候我的代码超时了dp时我 ...
随机推荐
- MapReduce自定义类输出的内容为内存地址
13480253104 mapreduce.KpiWritable@486a58c4 13502468823 mapreduce.KpiWritable@3de9d100 13560439658 ma ...
- Miller-Rabin素性测试(POJ3641)
一.概念引入 在以往判断一个数n是不是素数时,我们都是采用i从2到sqrt(n)能否整除n.如果能整除,则n是合数;否则是素数.但是该算法的时间复杂度为O(sqrt(n)),当n较大时,时间性能很差, ...
- glance image cache
The Glance Image Cache The Glance API server may be configured to have an optional local image cache ...
- Xamarin开发Android时Visual Studio 2012没有智能提示解决办法
Most of the people who work with Xamarin’s Mono for Android in Visual Studio 2012 face a bug where I ...
- 微信开发第8章 通过accesstoken将长连接转换为短链接
业务场景:开发的过程中经常会有一些很长的链接,这个时候如果生成二维码,会导致扫码的过程中识别比较慢,如果存入数据库,会导致数据库的字段长度要设定的很长才行,所以把长连接转换为短链接就越来越重要了. 接 ...
- 解决libcrypto.so.0.9.8: cannot open shared object file
文章解决的问题:安装nginx中需要libmysql.so.16包的支持,下面介绍如何安装,并建立lib的连接. 问题展示:error while loading shared libraries: ...
- get-random生成电话号码
"138"+((0..9|Get-Random -count 10) -join $null) From:http://blog.csdn.net/shrekz/article/d ...
- Microsoft.Net中数字签名技术
概 述:本文以Microsoft.Net中创建签名的CAB文件为例,说明如何创建签名文件,从而使你的代码能够安全的在INTERNET上公布.希望能起到抛转引玉的作用. keyword:数字签名,Act ...
- iOS开发之地图代理不起作用(提示vImage decode failed, falling back to CG path.)
项目中用到了地图相关的东西,就把曾经的demo搬了出来,结果发现直接执行之前的demo没有问题,在xcode5下新建项目再把代码粘贴过来就会提示 May 5 11:36:21 infomedia-iP ...
- VisJS 随机图
<!doctype html> <html> <head> <title>Random nodes</title> <style ty ...