【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 ...
随机推荐
- windbg命令详解
DLL 该扩展仅在内核模式下使用,即使它是在Ext.dll中的. Windows NT 4.0 Ext.dll Windows 2000 Ext.dll Windows XP和之后 Ext.dll ...
- codecomb 2098【stone】
题目描述 Description n个石堆围成一圈,提供两种操作: 1.每次将[L,R]堆的石子数量+k,其中,1<=L,R<=n,k>=0. 2.询问有最多石子的那一堆有多少石子. ...
- lowerCaseTableNames
数据库表,数据库名大小写铭感问题 mysql lower-case-table-names参数 线上有业务用到开源的产品,其中SQL语句是大小写混合的,而建表语句都是小写的,mysql默认设置导致这些 ...
- ORACLE 中写入txt文本与从Txt文件中读入数据 修改表结构
--创建一个表 DROP TABLE TEST CASCADE CONSTRAINTS ; CREATE TABLE TEST(A VARCHAR(30),B VARCHAR(30)); --查看具体 ...
- Java语言程序设计(基础篇) 第八章 多维数组
第八章 多维数组 8.2 二维数组的基础知识 二维数组中的元素通过行和列的下标来访问. 8.2.1 声明二维数组变量并创建二维数组 下面是二维数组的语法: 数据类型[][] 数组名; int[][] ...
- hdu 3061 (最大权闭合图)
分析:城池之间有依赖关系,汇点与能获得兵力的城池连接,容量为可以获得的兵力,损耗兵力的城池与汇点连接容量为损耗的兵力,有依赖关系的城池间连边,容量为无穷大,跑网络流求出的最小割就是损耗的最小兵力,,, ...
- Hibernate annotation多对多配置
角色(用户组),用户多对多. 角色实体配置: private Set<TAuthUser> users; @ManyToMany @JoinTable(name="t_auth_ ...
- 深入浅出:重温JAVA中接口与抽象的区别
抽象类:声明一个抽象类,就是在类的声明开头.在Class关键字的前面使用关键字abstract 下面定义一个抽象类,代码如下: abstract class A{ abstract void call ...
- PHP学习笔记七【函数】
<?php $a=13; function abc3($a) { unset($a);//[释放给定变量]表示不在abc3函数范围内,不在使用$a,后面需要全新定义 $a=45; } abc(3 ...
- c语言字符串比较函数strcmp
strcmp(s1,s2) 说明: 当s1<s2时,返回值<0 当s1=s2时,返回值=0 当s1>s2时,返回值>0两个字符串自左向右逐个字符相比(按ASCII值大小相比较) ...