【非经典】仙岛求药(二)

仙岛求药(二)

【试题描述】

咳,上回说到李逍遥去求药,其实他找到药之后,还需要给他的婶婶送过去,所以他需要在最短的时间内找到要并且到达婶婶(‘s‘)所在的位置。

【输入要求】

输入有多组测试数据. 每组测试数据以两个非零整数 M 和 N 开始,两者均不大于100。M 表示迷阵行数, N 表示迷阵列数。接下来有 M 行, 每行包含N个字符,不同字符分别代表不同含义: 
1) ‘@’:少年李逍遥所在的位置;
2) ‘.’:可以安全通行的方格;
3) ‘#’:有怪物的方格;
4) ‘*’:仙药所在位置。
5) ‘s‘ : 婶婶所在的位置。
    当在一行中读入的是两个零时,表示输入结束。

【输出要求】

对于每组测试数据,分别输出一行,该行包含李逍遥找到仙药并且送给婶婶需要穿过的最少的方格数目(计数包括初始位置的方块)。如果他不可能找到仙药, 则输出 -1。

【输入实例】

8 8
[email protected]##...#
#....#.#
#.#.##..
*.#.###.
#.#...#.
..###*#.
...#.s..
.#.*.###
0 0

  

【输出实例】

10

【其他说明】

【试题分析】

仔细一看,可以瞬间想到:遍历到每个仙药再回到婶婶,求最小值,那么只需要这么写:

#include<iostream>
#include<cstring>
using namespace std;
char map[101][101];
int a[101][101],ans=0,m,n;
void res(int u,int v,int i,int j)
{
    int t=a[u][v];
    if(u==i&&v==j) ans=t;
    t++;
    if(v<m-1&&map[u][v+1]!=‘#‘&&a[u][v+1]>t)
    {
        a[u][v+1]=t;
        res(u,v+1,i,j);
    }
    if(u>0&&map[u-1][v]!=‘#‘&&a[u-1][v]>t)
    {
        a[u-1][v]=t;
        res(u-1,v,i,j);
    }
    if(v>0&&map[u][v-1]!=‘#‘&&a[u][v-1]>t)
    {
        a[u][v-1]=t;
        res(u,v-1,i,j);
    }
    if(u<n-1&&map[u+1][v]!=‘#‘&&a[u+1][v]>t)
    {
        a[u+1][v]=t;
        res(u+1,v,i,j);
    }
}
int startx,starty,endx[10000],endy[10000],k=0,ans1=9999999,endx2,endy2,k2=0,ans2=9999999,sx,sy,ans3=9999999;
int main()
{
    while(scanf("%d%d",&n,&m)&&m!=0&&n!=0)
    {
        memset(a,1,sizeof(a));
        memset(endx,0,sizeof(endx));
        memset(endy,0,sizeof(endy));
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                scanf("%c",&map[i][j]);
                if(map[i][j]==‘@‘)
                {
                    startx=i;
                    starty=j;
                }
                if(map[i][j]==‘*‘)
			    {
                    endx[k]=i;
                    endy[k++]=j;
                }
                if(map[i][j]==‘s‘)
                {
                    endx2=i;
                    endy2=j;
                }
            }
            getchar();
        }
        memset(a,1,sizeof(a));
        a[startx][starty]=0;
        for(int i=0;i<k;i++)//枚举
        {
            memset(a,1,sizeof(a));//分段考虑
            a[startx][starty]=0;
            res(startx,starty,endx[i],endy[i]);
            ans1=ans,sx=endx[i],sy=endy[i];//赋值
            ans=0;
            memset(a,1,sizeof(a));
            a[sx][sy]=0;
            res(sx,sy,endx2,endy2);
            ans2=ans;
            if(ans3>ans2+ans1) ans3=ans1+ans2//更新;
            ans=0;
        }
        if(ans3<9999999)cout<<ans3<<endl;
        else cout<<-1;
        ans3=9999999;//更新
        ans1=9999999;
        ans2=9999999;
        k2=0;
        k=0;
    }
}

好了,写完代码了。

如果你认为对了,那么可以输入这组数据试一试

20 50

@*************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
**************************************************
*************************************************s

按这样来说,每一个*都要被算一次,这样的复杂度即使是喜欢打暴力的人也是不能容忍的,当然,改进算法才是我们的重点,但是怎样优化呢?

用BFS???

不好吧,那也会超限,但是正解+BFS是很快的。

用DFS???

更差了……

我们先来想一想这样一个问题,如果一个人要从上海开车去北京旅游,他会把所有路都傻傻的走一遍吗?当然不会了,因为人类有一张王牌——地图,我们可以去估一估最短距离。

那么这道题也是一样的,我们可以大概估一个两点之间最短距离值,算出它以后,因为我们默认为两点之间没有障碍,所以它就是最短路了,如果这样算的话还是大于等于原来我们记录的那个最小值,那实际的路径一定比这个估算最优值大或等于,由此,我们可以去除多个药的枚举,就像人人皆知的KMP算法一样,这种算法名叫启发式搜索,是人工智能的一个知识点,不一定要将它完全地写出代码,但至少要了解并会使用。

我们的代码只需要加上这么一行,就能大幅度提高效率(往往算法的精髓就在于此):

if(abs(endx[i]+endy[i]-startx-starty)+abs(endx2+endy2-endx[i]-endy[i])<ans3)

【代码】

#include<iostream>
#include<cstring>
using namespace std;
char map[101][101];
int a[101][101],ans=0,m,n;
void res(int u,int v,int i,int j)
{
    int t=a[u][v];
    if(u==i&&v==j) ans=t;
    t++;
    if(v<m-1&&map[u][v+1]!=‘#‘&&a[u][v+1]>t)
    {
        a[u][v+1]=t;
        res(u,v+1,i,j);
    }
    if(u>0&&map[u-1][v]!=‘#‘&&a[u-1][v]>t)
    {
        a[u-1][v]=t;
        res(u-1,v,i,j);
    }
    if(v>0&&map[u][v-1]!=‘#‘&&a[u][v-1]>t)
    {
        a[u][v-1]=t;
        res(u,v-1,i,j);
    }
    if(u<n-1&&map[u+1][v]!=‘#‘&&a[u+1][v]>t)
    {
        a[u+1][v]=t;
        res(u+1,v,i,j);
    }
}
int startx,starty,endx[10000],endy[10000],k=0,ans1=9999999,endx2,endy2,k2=0,ans2=9999999,sx,sy,ans3=9999999;
int main()
{
    while(cin>>n>>m&&m!=0&&n!=0)
    {
        memset(a,1,sizeof(a));
        memset(endx,0,sizeof(endx));
        memset(endy,0,sizeof(endy));
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                cin>>map[i][j];
                if(map[i][j]==‘@‘)
                {
                    startx=i;
                    starty=j;
                }
                if(map[i][j]==‘*‘)
			    {
                    endx[k]=i;
                    endy[k++]=j;
                }
                if(map[i][j]==‘s‘)
                {
                    endx2=i;
                    endy2=j;
                }
            }
        }
        memset(a,1,sizeof(a));
        a[startx][starty]=0;
        for(int i=0;i<k;i++)
        {
            if(abs(endx[i]+endy[i]-startx-starty)+abs(endx2+endy2-endx[i]-endy[i])<ans3)            //判定,如果此代码其它地方不理解可以去看仙岛求药(一)
            {
                memset(a,1,sizeof(a));
                a[startx][starty]=0;
                res(startx,starty,endx[i],endy[i]);
                ans1=ans,sx=endx[i],sy=endy[i];
                ans=0;
                memset(a,1,sizeof(a));
                a[sx][sy]=0;
                res(sx,sy,endx2,endy2);
                ans2=ans;
                if(ans3>ans2+ans1) ans3=ans1+ans2;
                ans=0;
            }
        }
        cout<<ans3<<endl;
        ans3=9999999;
        ans1=9999999;
        ans2=9999999;
        k2=0;
        k=0;
    }
}
时间: 2024-12-31 01:25:22

【非经典】仙岛求药(二)的相关文章

【经典】仙岛求药(一)

仙岛求药(一) [试题描述] 少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶.叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处.迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全.现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他.现在要求你来帮助他实现这个目标.      下图显示了一个迷阵的样例及李逍遥找到仙药的路线. [输入要求] 输入有多组测试数据. 每

仙岛求药

#include<iostream> #include<cmath> #include<cstdio> #include<algorithm> #include<queue> using namespace std; char map[101][101]; int step=9999999; int yaox[10010],yaoy[10010],book[101][101],n,m; int road(int a,int b,int x,int

OpenJudge 2727 仙岛求药

总时间限制:  1000ms 内存限制:  65536kB 描述 少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶.叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处.迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全.现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他.现在要求你来帮助他实现这个目标.下图 显示了一个迷阵的样例及李逍遥找到仙药的路线. 什么鬼题面!

2727:仙岛求药(广搜)

2727:仙岛求药 总时间限制: 1000ms 内存限制: 65536kB 描述 少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶.叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处.迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全.现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他.现在要求你来帮助他实现这个目标.下图 显示了一个迷阵的样例及李逍遥找到仙药的路线

仙岛求药(一)

仙岛求药(一) 难度级别:B: 运行时间限制:1000ms: 运行空间限制:256000KB: 代码长度限制:2000000B 试题描述 少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶.叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处.迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全.现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他.现在要求你来帮助他实现这

AC日记——仙岛求药 openjude 2727

仙岛求药 思路: bfs: 来,上代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxm 10005 const int dx[5]={0,-1,0,1,0}; const int dy[5]={0,0,1,0,-1}; int n,m,quex[maxm],quey[maxm],qu

NOI 2727:仙岛求药 x

总时间限制:  1000ms 内存限制:  65536kB 描述 少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶.叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处.迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全.现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他.现在要求你来帮助他实现这个目标.下图 显示了一个迷阵的样例及李逍遥找到仙药的路线. 输入 输入有

[广度优先搜索]仙岛求药

题目大意 题目描述 少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶.叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处.迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全.现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他.现在要求你来帮助他实现这个目标. 下图 显示了一个迷阵的样例及李逍遥找到仙药的路线. 输入格式 输入有多组测试数据. 每组测试数据以两个非零整

(搜索)2727:仙岛求药【待完成】

描述 少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶.叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处.迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全.现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他.现在要求你来帮助他实现这个目标. 输入 输入有多组测试数据. 每组测试数据以两个非零整数 M 和 N 开始,两者均不大于20.M 表示迷阵行数, N 表示迷