hrbust 1286/哈理工oj 1286 迷宫与宝藏【较难bfs】

迷宫与宝藏
Time Limit: 2000 MSMemory Limit: 65536 K
Total Submit: 141(26 users)Total Accepted: 31(20 users)Rating: Special Judge: No
Description
机器人要在一个矩形迷宫里行动(不能原地停留,只能走向上///右),每移动一格花费1个单位时间。

迷宫有以下几种元素:

* 机器人的起点

# 墙。机器人不能走过这些格子

. 平地。机器人可以在上面自由行走

0-9 宝藏。当机器人走到此处会立刻获得该数字相应的宝藏,宝藏不会消失,可以反复获取(但不能停留)

若机器人要恰好获得总和为x的宝藏,它最少需要多少时间?

Input
第一行输入任务数量T, 接下来有T个任务

每块第一行有两个整数, n(0 <
100), m(0 < 100), 表示迷宫有n+1行和m+1

接下来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;
}









  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值