【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 ...
随机推荐
- build.gradle中引入jar
只需在 dependencies{}中添加: compile fileTree(include: ['*.jar'], dir: 'libs')
- UESTC_Ferris Wheel String 2015 UESTC Training for Search Algorithm & String<Problem L>
L - Ferris Wheel String Time Limit: 3000/1000MS (Java/Others) Memory Limit: 43000/43000KB (Java/ ...
- vector迭代器失效的一种情形
使用过STL的人都应该知道关于迭代器失效的原理,这里以后vector迭代器失效为例: 第一种:当插入一个元素到vector中,如果插入后容器已满,那么容器将新开辟一块内存区域,然后 将原内存中的数据拷 ...
- 一、crond简介
crond 是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务 工具,并且会自动启动crond进程,cro ...
- IOS Xmpp的Iq发送和解析之请求讨论组成员
#pragma mark - 获取讨论组成员 - (void)getDiscussionMemberInfoWithSendIq:(discussionModel *)data { NSXMLElem ...
- 走进C++程序世界------继承和派生
继承和派生 继承是面向对象编程语言的最重要方面之一,正确的使用继承可编写出设计良好,容易于维护和扩展的应用程序.下面是在其他博客中的总结: ****************************** ...
- Android漫游记(1)---内存映射镜像(memory maps)
Android系统内核基于Linux2.6+内核,因此,其在进程内存管理方面的非常多机制和Linux是非常相像的.首先,让我们来看一个典型的Android进程的内存镜像(App进程和Native本地进 ...
- Ffplay视频播放流程
主框架流程 下图是一个使用“gcc+eygpt+graphviz+手工调整”生成的一个ffplay函数基本调用关系图,其中只保留了视频部分,去除了音频处理.字幕处理以及一些细节处理部分. 注:图中的数 ...
- bootstrap插件小记
1.模态框 除了通过data-toggle和data-target来控制模态弹出窗之外,Bootstrap框架针对模态弹出框还提供了其他自定义data-属性,来控制模态弹出窗.比如说:是否有灰色背景m ...
- PL/SQL分页查询
create or replace procedure fenye(tabelname in varchar2,currentpage in number,pageSize in number,inW ...