zoukankan      html  css  js  c++  java
  • 【非经典】仙岛求药(二)

    仙岛求药(二)

    【试题描述】

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

    【输入要求】

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

    【输出要求】

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

    【输入实例】

    8 8
    .@##...#
    #....#.#
    #.#.##..
    *.#.###.
    #.#...#.
    ..###*#.
    ...#.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???

        更差了……

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

        那么这道题也是一样的,我们可以大概估一个两点之间最短距离值,算出它以后,因为我们默认为两点之间没有障碍,所以它就是最短路了,如果这样算的话还是大于等于原来我们记录的那个最小值,那实际的路径一定比这个估算最优值大或等于,由此,我们可以去除多个药的枚举剪枝。

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

    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; } }
  • 相关阅读:
    一个涉及到浮点寄存器的CM
    树和二叉树一篇就搞定!
    串的两种模式匹配方式(BF/KMP算法)
    队列的知识讲解与基本实现(数据结构)
    如何用C++实现栈
    判断List集合为空还是null的正确打开方式
    双链表的基本实现与讲解(C++描述)
    Redis从认识安装到实现增删改查
    如何使用C++实现单链表
    线性表——顺序表的实现与讲解(C++描述)
  • 原文地址:https://www.cnblogs.com/wxjor/p/5697428.html
Copyright © 2011-2022 走看看