迷宫与宝藏 | ||||||
| ||||||
Description | ||||||
机器人要在一个矩形迷宫里行动(不能原地停留,只能走向上/下/左/右),每移动一格花费1个单位时间。 迷宫有以下几种元素: 【*】 机器人的起点 【#】 墙。机器人不能走过这些格子 【.】 平地。机器人可以在上面自由行走 【0-9】 宝藏。当机器人走到此处会立刻获得该数字相应的宝藏,宝藏不会消失,可以反复获取(但不能停留) 若机器人要恰好获得总和为x的宝藏,它最少需要多少时间? | ||||||
Input | ||||||
第一行输入任务数量T, 接下来有T个任务 每块第一行有两个整数, n(0 < 接下来n行输入迷宫 最后一行输入你要收集的宝藏的总价值x(x ≤ 100) | ||||||
Output | ||||||
对于每个任务,输出最少花费的时间,如果完成不了该任务则输出-1 | ||||||
Sample Input | ||||||
3 2 3 1.#2 #..# *.#. 3 2 3 2.#2 #..# *.#. 5 2 3 2.#2 #.3# *.#. 5 | ||||||
Sample Output | ||||||
8 -1 6 |
做过不少这样的带着宝贝走的图搜的题、可惜这个题卡时间卡的太狠、剪枝无果、换思路、卡内存、优化代码、无果。如果是条件再放松一点点、这个题就好A多了、真的是一道很好的题目、卡时间,数据,内存具备、而且还有一个坑点、这里一会说(虽然找到了,但是还是没能AC)、最后还是看的大牛代码,找到了最终优化的方法、
首先呢,这里的路是可以重复行走的,但是行走重复的时候一定要保证身上带的东西的数量不同,要不然没有重复走这个路的必要、所以这里vis数组是三维的
分别存三个值,x,y,v(宝藏的价值)、
然后就bfs遍历就行了、优化几个小小的点:
1.起始点要注意是可以重复行走的、
2.如果当前走到的地方的宝藏数量大于了目标量,continue;
3.不用优先队列会超时、用了优先队列会超内存、、、、、妈了个巴子,不让人过个好年,好歹也是除夕,能不能愉快的玩耍了;
然后贴上我的思路的代码,一会和大牛的对比:
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
struct zuobiao
{
int x,y,output;
int cur;
/*friend bool operator <(zuobiao a,zuobiao b)
{
return a.cur>b.cur;
}*/
}now,nex;
char a[101][101];
int vis[101][101][101];
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
int n,m;
int mubiao;
void bfs(int x,int y)
{
queue<zuobiao >s;
//priority_queue<zuobiao>s;
memset(vis,0,sizeof(vis));
vis[0][x][y]=1;
now.x=x;
now.y=y;
now.output=0;
now.cur=0;
s.push(now);
while(!s.empty())
{
now=s.front();
s.pop();
for(int i=0;i<4;i++)
{
nex.x=now.x+fx[i];
nex.y=now.y+fy[i];
nex.output=now.output;
nex.cur=now.cur;
if(nex.x>=0&&nex.x<n&&nex.y>=0&&nex.y<m&&vis[nex.output][nex.x][nex.y]==0&&a[nex.x][nex.y]!='#')
{
if(a[nex.x][nex.y]=='.')
{
nex.cur++;
vis[nex.output][nex.x][nex.y]=1;
s.push(nex);
}
if(a[nex.x][nex.y]>='0'&&a[nex.x][nex.y]<='9')
{
nex.cur++;
nex.output+=a[nex.x][nex.y]-'0';
//printf("%d\n",nex.output);
if(nex.output==mubiao)
{
printf("%d\n",nex.cur);
return ;
}
if(nex.output>mubiao)
continue;
vis[nex.output][nex.x][nex.y]=0;
s.push(nex);
}
}
}
}
printf("-1\n");
return ;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d %d%",&n,&m);
n++;
m++;
int x,y;
for(int i=0;i<n;i++)
{
scanf("%s",a[i]);
for(int j=0;j<m;j++)
{
if(a[i][j]=='*')
{
x=i;
y=j;
a[i][j]='.';
}
}
}
scanf("%d",&mubiao);
if(mubiao==0)
{
printf("0\n");
continue;
}
bfs(x,y);
}
}
以上是我的代码和简单叙述的思路、可惜怎么做都不能AC掉、水平实力还是不够啊....................T T
然后我们来说大牛的代码:大牛博客传送门:http://www.cnblogs.com/-hsz/archive/2012/11/09/2762050.html
首先,他的思路是这样的:把*和.都变成0
int x,y;
for(int i = 0; i < n; i++)
{
scanf("%s",map[i]);
for(int j=0; j<m; j++)
{
if(map[i][j]=='*')
{
map[i][j]='0';
x=i;
y=j;
}
if(map[i][j]=='.')
{
map[i][j]='0';
}
}
}
然后考虑时间最优,这里咱俩的做题思路是一样的,但是我的超内存、(因为我的队列里边存的东西太多了、剪枝无果)他的没有,他是怎么做的呢?
他用一个vis【】【】【】表示:行,列,价值,记录最小步数。那么他是如何保证是最小步数的呢?
我们对应代码来看:
int vis[101][101][101]; //行,列,价值。vis记录最小步数。首先对他初始化为INT_MAX(其实初始化足够大就行,不用这么完美)
char map[101][101];
int n, m, mubiao;
struct zuobiao
{
int x, y, v;
}now,nex;
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
int bfs(int x,int y)
{
now.x=x;
now.y=y;
now.v=0;
vis[now.x][now.y][now.v] = 0;
queue<struct zuobiao> s;
s.push(now);//起点入队
while(!s.empty())
{
struct zuobiao now=s.front();
s.pop();
for(int i = 0; i < 4; i++)
{
nex.x = now.x + fx[i];
nex.y = now.y + fy[i];
if(nex.x >= 0 && nex.x < n && nex.y >= 0 && nex.y < m && map[nex.x][nex.y] != '#')//符合条件的走向
{
nex.v = now.v + map[nex.x][nex.y] - '0';//宝藏入身、
if(nex.v == mubiao) return vis[now.x][now.y][now.v] + 1;//如果达到了目标数,输出
if(nex.v > mubiao) continue;//剪枝
if(vis[nex.x][nex.y][nex.v] > vis[now.x][now.y][now.v] + 1)//这里注意 ,很完美的语句,既达到了优先队列的效果(但是也不能说完全一样),也达到了时间数组的作用、注意初始化很大、
{
vis[nex.x][nex.y][nex.v] = vis[now.x][now.y][now.v] + 1;
s.push( nex );
}
}
}
}
return -1;
}
然后上完整的AC代码:
#include<stdio.h>
#include<limits.h>
#include<string.h>
#include<queue>
using namespace std;
int vis[101][101][101]; //行,列,价值。vis记录最小步数。
char map[101][101];
int n, m, mubiao;
struct zuobiao
{
int x, y, v;
}now,nex;
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
int bfs(int x,int y)
{
now.x=x;
now.y=y;
now.v=0;
vis[now.x][now.y][now.v] = 0;
queue<struct zuobiao> s;
s.push(now);
while(!s.empty())
{
struct zuobiao now=s.front();
s.pop();
for(int i = 0; i < 4; i++)
{
nex.x = now.x + fx[i];
nex.y = now.y + fy[i];
if(nex.x >= 0 && nex.x < n && nex.y >= 0 && nex.y < m && map[nex.x][nex.y] != '#')
{
nex.v = now.v + map[nex.x][nex.y] - '0';
if(nex.v == mubiao) return vis[now.x][now.y][now.v] + 1;
if(nex.v > mubiao) continue;
if(vis[nex.x][nex.y][nex.v] > vis[now.x][now.y][now.v] + 1)
{
vis[nex.x][nex.y][nex.v] = vis[now.x][now.y][now.v] + 1;
s.push( nex );
}
}
}
}
return -1;
}
int main()
{
int T;
scanf("%d", &T);
while( T-- )
{
scanf("%d %d", &n, &m);
n++;
m++;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
for(int k = 0; k < 101; k++)
{
vis[i][j][k] =0x1f1f1f1f;
}
}
}
int x,y;
for(int i = 0; i < n; i++)
{
scanf("%s",map[i]);
for(int j=0; j<m; j++)
{
if(map[i][j]=='*')
{
map[i][j]='0';
x=i;
y=j;
}
if(map[i][j]=='.')
{
map[i][j]='0';
}
}
}
scanf("%d", &mubiao);
if(mubiao == 0)
{
printf("0\n");
}
else
{
printf("%d\n", bfs(x,y));
}
}
return 0;
}