【HDU 4771 Stealing Harry Potter's Precious】BFS+状压
2013杭州区域赛现场赛二水。。。
类似“胜利大逃亡”的搜索问题,有若干个宝藏分布在不同位置,问从起点遍历过所有k个宝藏的最短时间。
思路就是,从起点出发,搜索到最近的一个宝藏,然后以这个位置为起点,搜索下一个最近的宝藏,直至找到全部k个宝藏。有点贪心的感觉。
由于求最短时间,BFS更快捷,但耗内存,这道题就卡在这里了。。。
这里记录了我几次剪枝的历史。。。题目要求内存上限32768KB,就差最后600KB了。。。但我从理论上觉得已经不能再剪了,留下的结点都是盲目式搜索必然要访问的结点。

在此贴上剪枝到33292KB的代码,注释里说明了剪掉的部分(剪枝策略可能还有不正确的地方,仅供参考)
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAX_N=;
int n,m,k;
int sx,sy;
int dx[]={,,,-},dy[]={,-,,};
char G[MAX_N+][MAX_N+];
int cur_step; struct Node
{
int x,y,step,g;//每个点的坐标、走到这里的步数、到这一点已找到的宝藏数
Node(){}
Node(int xx,int yy,int ss,int gg):x(xx),y(yy),step(ss),g(gg){}
}; int bfs()
{
int get=;//全局已找到的宝藏数,小于这个数的结点视为“过期”
queue<Node> que;
que.push(Node(sx,sy,,));
cur_step=-;//全局当前的步数
while(!que.empty())
{
Node cur=que.front();
que.pop();
//当此点步数小于等于到达某一个宝藏的步数(即和这个宝藏在搜索树中“同层”),剪枝
if(cur.step<=cur_step&&cur.g<get) continue;
int has=;//这一点的四个可扩展结点中有没有是宝藏的
for(int i=;i<;i++)
{
int nx=cur.x+dx[i];
int ny=cur.y+dy[i];
if(nx<||ny<||nx>n||ny>m) continue;
if(G[nx][ny]=='#') continue;
if(cur.g<get) continue;//宝藏数过期
if(G[nx][ny]=='*')//是宝藏
{
has++;
sx=nx;
sy=ny;
}
}
if(has)
{//一旦四个结点有一个是宝藏,则只将这一个入队,由于没有设vis数组!!!同层其他宝藏会在之后被推入队列的
cur.g++;
get=cur.g;
G[sx][sy]='.';
cur_step=cur.step+;
if(get==k) return cur.step+;
que.push(Node(sx,sy,cur.step+,cur.g));
continue;
}
//当四个结点都不是宝藏时,根据盲目式搜索策略,只好全部推入队列,就在这里推入了很多无用但又无法提前预知的点
for(int i=;i<;i++)
{
int nx=cur.x+dx[i];
int ny=cur.y+dy[i];
if(nx<||ny<||nx>n||ny>m) continue;
if(G[nx][ny]=='#') continue;
if(cur.g<get) continue;
que.push(Node(nx,ny,cur.step+,cur.g));
}
}
if(get<k) return -;
} int main()
{
//freopen("1002.txt","r",stdin);
while((scanf("%d%d",&n,&m)!=EOF)&&(!(n==&&m==)))
{
for(int i=;i<=n;i++)
{
getchar();
for(int j=;j<=m;j++)
{
scanf("%c",&G[i][j]);
if(G[i][j]=='@')
{
sx=i;
sy=j;
}
}
}
scanf("%d",&k);
for(int i=;i<k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
G[x][y]='*';//宝藏标记为*
}
printf("%d\n",bfs());
}
return ;
}
BFS 剪枝 无状压
就在无计可施时,队友建议我改用DFS,但目测会超时或爆栈;而且总觉得原思路是对的,问题在优化上。
这时重新看题,发现我居然忽略了一个条件k<=4~~~~~~~~~~有经验的人大概早就会想到状压了,然而我经验还太少,并是没想到,想到了也写不出来。。。
之后找题解,发现很多是用BFS求出起点和宝藏的最短路,然后再DFS或枚举所有路径。并是没有想通这个思路。
再找,终于发现有和我相同思路的了,感谢原作者~ http://blog.csdn.net/u011932355/article/details/40819317 于是参照这篇把自己的改成了状压版,然后,就得到了内存和时间都少了一个数量级的结果。。。状压大法好~

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAX_N=;
int n,m,k;
int sx,sy,goal;
int dx[]={,,,-},dy[]={,-,,};
int b[]={,,,,};//原作者的写法,这个数组有大用~
char G[MAX_N+][MAX_N+];
int vis[MAX_N+][MAX_N+][];//第三维是状压,表示当前已得宝藏状态 1111~0000
char str[]; struct Node
{
int x,y,step,get;//走到这一点的步数,到这一点的已得宝藏状态
Node(){}
Node(int xx,int yy,int ss,int gg):x(xx),y(yy),step(ss),get(gg){}
}; int bfs()
{
memset(vis,,sizeof(vis));
queue<Node> que;
que.push(Node(sx,sy,,));
vis[sx][sy][]=;
while(!que.empty())
{
Node cur=que.front();
que.pop();
for(int i=;i<;i++)
{
int nx=cur.x+dx[i];
int ny=cur.y+dy[i];
if(nx<||ny<||nx>n||ny>m) continue;
if(G[nx][ny]=='#') continue; Node next(nx,ny,cur.step+,cur.get);
if(G[nx][ny]<=) next.get|=G[nx][ny];//找到一处宝藏,追加到当前宝藏状态
if(vis[nx][ny][next.get]) continue;//同一点同一宝藏状态,只入队一次
vis[nx][ny][next.get]=;
if(next.get==goal) return next.step;//找到全部宝藏,返回
que.push(next);
}
}
return -;
} int main()
{
//freopen("1002.txt","r",stdin);
while(scanf("%d%d",&n,&m)==)
{
if(n==&&m==) break;
for(int i=;i<=n;i++)
{
scanf("%s",str);
int cnt=;
for(int j=;str[j]!='\0';j++)
{
if(str[j]=='#') G[i][cnt++]='#';
else if(str[j]=='.') G[i][cnt++]='.';
else if(str[j]=='@')
{
G[i][cnt++]='@';
sx=i;
sy=cnt-;
}
}
}
scanf("%d",&k);
goal=b[k]-;//通过k和b数组得到目标状态(1111,111,11,1,0之一)
while(k--)
{//按先后顺序,把每个宝藏所在点的状态设为(1,10,100,1000之一)
int x,y;
scanf("%d%d",&x,&y);
G[x][y]=b[k];
}
printf("%d\n",bfs());
}
return ;
}
这个状压是把当前的宝藏访问状态作为vis数组的第三维存入,这样用vis数组对于我来说是第一次,而我的无状压版本大概就是因为没设vis数组,所以本身就增加了很多无用节点的推入,取出后再丢弃就不叫剪枝了。。。因为还是被推入队列了。。。
比赛时一直纠结一道题是我的不对,感谢两位队友的指教。
场下这样的过程还是比较利于学习的,关键是在纠结的过程中暴露了自己哪些基础知识还不清楚,给自己提供了补习的方向。
【HDU 4771 Stealing Harry Potter's Precious】BFS+状压的更多相关文章
- hdu 4771 Stealing Harry Potter's Precious (BFS+状压)
题意: n*m的迷宫,有一些格能走("."),有一些格不能走("#").起始点为"@". 有K个物体.(K<=4),每个物体都是放在& ...
- HDU 4771 Stealing Harry Potter's Precious (2013杭州赛区1002题,bfs,状态压缩)
Stealing Harry Potter's Precious Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 ...
- HDU 4771 Stealing Harry Potter's Precious dfs+bfs
Stealing Harry Potter's Precious Problem Description Harry Potter has some precious. For example, hi ...
- HDU 4771 Stealing Harry Potter's Precious
Stealing Harry Potter's Precious Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 ...
- HDU Stealing Harry Potter's Precious(状压BFS)
状压BFS 注意在用二维字符数组时,要把空格.换行处理好. #include<stdio.h> #include<algorithm> #include<string.h ...
- hdu 4771 Stealing Harry Potter's Precious (2013亚洲区杭州现场赛)(搜索 bfs + dfs) 带权值的路径
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4771 题目意思:'@' 表示的是起点,'#' 表示的是障碍物不能通过,'.' 表示的是路能通过的: ...
- hdu 4771 Stealing Harry Potter's Precious(bfs)
题目链接:hdu 4771 Stealing Harry Potter's Precious 题目大意:在一个N*M的银行里,贼的位置在'@',如今给出n个宝物的位置.如今贼要将全部的宝物拿到手.问最 ...
- Stealing Harry Potter's Precious BFS+DFS
Problem Description Harry Potter has some precious. For example, his invisible robe, his wand and hi ...
- HDU 5025:Saving Tang Monk(BFS + 状压)
http://acm.hdu.edu.cn/showproblem.php?pid=5025 Saving Tang Monk Problem Description <Journey to ...
随机推荐
- 【Xamarin挖墙脚系列:Xamarin.IOS的多个Storybord分隔视图的导航】
在实际开发中,我是推荐使用画板Storybord的.也可以适当的添加xib进行界面的绘制.尽量不要用或者少用代码绘制视图.(少一些装B分子,可以极大的缩短项目的周期). 不要讲性能,不是不讲性能,ap ...
- g++ error: expected nested-name-specifier before 'XXX'
template <typename addrT=int,typename valuT=int,typename stream_addrT=bm_addr,typename stream_siz ...
- jQuery中append html后绑定事件不起作用
事件一定要紧跟append之后, 否则append元素点击不起作用 $(function(){$('div').append('<ul><li id="appli" ...
- Co-prime Array&&Seating On Bus(两道水题)
Co-prime Array Time Limit:1000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Su ...
- 使用SOAPUI测试WEBAPI接口
其实很简单,用起来也挺方便的. 首先,先去百度下SoapUI 下载,下载安装好后,打开软件. 1.新建一个项目 点击 Create Empty Project 按钮后,会自动新建一个项目,名字默认为: ...
- 远程调试weinre的使用
一.用途 *鉴于在浏览器调试移动端页面无法准确反映移动端实际情况并无法高效调试,故常常使用远程调试工具通过电脑连接手机进行调试,常用远程调试方式: 1.chrome连接安卓机远程调试 2.Mac连接苹 ...
- Javascript进阶篇——(JS基础语法)笔记整理
根据慕课网学习整理到一起的笔记,把东西整理到一起看起来比较方便 什么是变量字面意思:变量是可变的量:编程角度:变量是用于存储某种/某些数值的存储器.我们可以把变量看做一个盒子,盒子用来存放物品,物品可 ...
- .NET一个线程更新另一个线程的UI(两种实现方法及若干简化)
Winform中的控件是绑定到特定的线程的(一般是主线程),这意味着从另一个线程更新主线程的控件不能直接调用该控件的成员. 控件绑定到特定的线程这个概念如下: 为了从另一个线程更新主线程的Window ...
- SQL中Truncate的用法(转)
转自:http://www.studyofnet.com/news/555.html 本文导读:删除表中的数据的方法有delete,truncate, 其中TRUNCATE TABLE用于删除表中的所 ...
- JavaScript的对象——灵活与危险
转:http://www.cnblogs.com/tolg/p/4873000.html 没有哪种数据结构比JavaScript的对象更简单灵活了.作为一个弱动态类型语言,JavaScript对对象的 ...