一道广搜寻路题

同样是在qq群里看到的题目,想了好久算法,实现也用了很久。

关于题目首先看图:

总的来说,就是一个二维迷宫的寻路,迷宫中有对应的钥匙和刺,每走一步会消耗1点Hp,当走到刺上时会额外消耗100点hp,持有对应颜色的钥匙通过刺时不用额外消耗Hp。

给予起点和终点的坐标,,输出移动方式,让人物抵达终点所消耗的Hp尽可能的小。

例子:

3 3 1
..a
##A
...
1 1
3 3
这个是输入数据 第一个代表高 第二个宽 第三个是钥匙和陷阱的对数 .代表平地 #代表墙 小写字母是钥匙 大写字母是对应的陷阱
输出为
R
R
D
D

接下去是算法考虑:

关于迷宫寻路,我的第一反应自然是bfs(广搜),比起dfs(深搜),广搜有着每个点仅遍历一次的优点。

比起传统的迷宫寻路,这个迷宫寻路多了钥匙和陷阱方面的因素,在面对陷阱时如何找到对应的钥匙是算法的关键。

这里给上我个人的思路:

通过从小到大增加自身hp,不断的找到可以抵达的地点,一旦找到了终点,那么停止程序输出结果。

对于钥匙和普通地面,将其加入“可抵达点”集合时,与普通bfs的做法相同即可。

对于刺,首先判断如果此时自身的Hp已经有了101多余,那么意味着无需去寻找钥匙,直接强行传过刺是更好的选择。

hp多余不足101的场合,查看能否抵达刺对应钥匙的坐标?(即钥匙是否已经被找到)

如果没找到钥匙,那么就跳过,否则,以钥匙为起点进行第二次寻路。这次寻路的目标是,找到“起点到刺的路径”,所以要对刺在的位置进行一次回溯,获取之前走过的所有路径,然后再进行一次简单的dfs,获取dfs的路程,那么获取钥匙所需的hp就是这次dfs的长度

*2。

由于钥匙到刺的路径是固定的,而你去取钥匙过刺的话,需要进行很多次判定(直到hp增加到够你取钥匙为止)(如果你没看懂这句话,说明没有看懂这个算法...),所以我们也可以把钥匙到刺的路径保存起来,这样能省很多效率。

下面是我写的代码,写的很丑陋请见谅,可以看到有很多被注释的输出用的语句...因为那时候电脑上没有编译环境,就下了一个轻量级的,结果不能debug...

using System;
using System.Collections.Generic;
class Program
{
    static int[,] map;//记录地图元素
    static int[,] s_map;//记录步数
    static int height,width,key_num,start_x,start_y,end_x,end_y;
    static int[,] key;//记录钥匙对应的刺所在的位置
    static string[] s_key;//记录对应钥匙到达刺所需的移动方式,输出用
    static List<int> list=new List<int>();//bfs中可拓展的节点
    static List<int> outlist=new List<int>();//记录找钥匙时插入的节点,输出用
    static List<int> outlist2=new List<int>();
    static bool find=false;
    static void Main(string[] args){
        getinput();
        //output();
        list.Add(start_x*width+start_y);
        s_map[start_x,start_y]=0;
        findway(0);
        /*for (int i=0;i<height;i++){
            Console.WriteLine();
            for (int j=0;j<width;j++){
                Console.Write(s_map[i,j]+" ");
            }
        }*/
        //Console.WriteLine(s_map[end_x,end_y]);
        //int step=s_map[end_x,end_y];
        int x=end_x,y=end_y;
        string output="";
        while(true){
            //Console.WriteLine(x+" "+y+" "+output);
            //Console.ReadKey();
            if ((x==start_x)&&(y==start_y)) break;
            for(int i=outlist.Count-1;i>=0;i--){
                int ox=outlist[i]/width;
                int oy=outlist[i]%width;
                //Console.WriteLine(ox+" "+oy);
                //Console.ReadKey();
                if(x==ox&&y==oy){
                /*for (int j=0;j<outlist.Count;j++){
                    Console.WriteLine(outlist.Count+" "+outlist[j]+" "+outlist2[j]+" "+s_key[outlist2[j]]);
                }*/
                string s=s_key[outlist2[i]];

                    output+=s;

                for (int j=s.Length-1;j>=0;j--){
                    if (‘L‘==s[j]) output+="R";
                    if (‘R‘==s[j]) output+="L";
                    if (‘D‘==s[j]) output+="U";
                    if (‘U‘==s[j]) output+="D";
                }
                outlist.RemoveAt(i);
                outlist2.RemoveAt(i);
                //Console.WriteLine(output+" "+outlist.Count);
                }
            }
            if ((map[x,y]>0)&&map[x,y]<30){//刺的场合
                if (x>0) if(s_map[x-1,y]==s_map[x,y]-101){x=x-1;output+="D";continue;}
                if (y>0) if(s_map[x,y-1]==s_map[x,y]-101){y=y-1;output+="R";continue;}
                if (x<height-1) if (s_map[x+1,y]==s_map[x,y]-101){x=x+1;output+="U";continue;}
                if (y<width-1) if (s_map[x,y+1]==s_map[x,y]-101){y=y+1;output+="L";continue;}//如果不是强穿的话

                if (x>0) if(s_map[x-1,y]==s_map[x,y]-key[map[x,y],4]*2-1){outlist.Add(key[map[x,y],5]);outlist2.Add(map[x,y]);x=x-1;output+="D";continue;}
                if (y>0) if(s_map[x,y-1]==s_map[x,y]-key[map[x,y],4]*2-1){outlist.Add(key[map[x,y],5]);outlist2.Add(map[x,y]);y=y-1;output+="R";continue;}
                if (x<height-1) if (s_map[x+1,y]==s_map[x,y]-key[map[x,y],4]*2-1){outlist.Add(key[map[x,y],5]);outlist2.Add(map[x,y]);x=x+1;output+="U";continue;}
                if (y<width-1) if (s_map[x,y+1]==s_map[x,y]-key[map[x,y],4]*2-1){outlist.Add(key[map[x,y],5]);outlist2.Add(map[x,y]);y=y+1;output+="L";continue;}
            }
            else{
                //Console.WriteLine(s_map[x,y+1]+" "+s_map[x,y]);
            if (x>0) if(s_map[x-1,y]==s_map[x,y]-1){x=x-1;output+="D";continue;}
            if (y>0) if(s_map[x,y-1]==s_map[x,y]-1){y=y-1;output+="R";continue;}
            if (x<height-1) if (s_map[x+1,y]==s_map[x,y]-1){x=x+1;output+="U";continue;}
            if (y<width-1) if (s_map[x,y+1]==s_map[x,y]-1){y=y+1;output+="L";continue;}
            }
        }
        for (int i=output.Length-1;i>=0;i--)
            Console.WriteLine(output[i]);
        Console.ReadKey();
    }
    static void findway(int hp){
        int x,y;
        bool bo;
        for (int i=list.Count-1;i>=0;i--){
            x=list[i]/width;
            y=list[i]%width;
            bo=true;
            if (x>0)  if (!check(x-1,y,hp,s_map[x,y])) bo=false;
            if (y>0)  if (!check(x,y-1,hp,s_map[x,y])) bo=false;
            if (x<height-1)  if (!check(x+1,y,hp,s_map[x,y])) bo=false;
            if (y<width-1)  if (!check(x,y+1,hp,s_map[x,y])) bo=false;
            if (bo) list.RemoveAt(i);
        }
        if (!find) findway(++hp);
    }
    static bool check(int x,int y,int hp,int v){
        //Console.WriteLine(x+" "+y+" "+hp+" "+v+" "+find);
        //Console.WriteLine((x)+" "+(y)+" "+map[x,y]+" "+hp+" "+list.Count);
        if ((x==end_x)&&(y==end_y))find=true;
        if (s_map[x,y]!=-1) return true;
        if (-1==map[x,y]) {return true;}else
        if (0==map[x,y]){//普通地面的情况下
            if (hp==v+1){
                s_map[x,y]=hp;
                list.Add(x*width+y);
                //Console.WriteLine(x+" "+y);
                //Console.ReadKey();
                return true;
            }else {return false;}
        }else
        if (map[x,y]<30){//刺的情况下
            if (hp==v+101){//能否强穿?
                s_map[x,y]=hp;
                list.Add(x*width+y);
                //Console.WriteLine(x+" "+y);
                //Console.ReadKey();
                return true;
            }else{//去找钥匙
                int key_x=key[map[x,y],0];
                int key_y=key[map[x,y],1];//获取钥匙坐标
                if(s_map[key_x,key_y]!=-1){//是否找到了钥匙?
                    if (key[map[x,y],4]==0)//将bfs得到的数据保存起来
                    {
                        bool[,] boolmap=new bool[height,width];
                        int bo_x=x,bo_y=y;
                        s_map[x,y]=100000;
                        while (!(bo_x==start_x&&bo_y==start_y)){
                            //Console.WriteLine(bo_x+" "+bo_y);
                            //Console.ReadKey();
                            boolmap[bo_x,bo_y]=true;
                            if (bo_x>0) if(s_map[bo_x-1,bo_y]<s_map[bo_x,bo_y]&&s_map[bo_x-1,bo_y]!=-1){bo_x=bo_x-1;continue;}
                            if (bo_y>0) if (s_map[bo_x,bo_y-1]<s_map[bo_x,bo_y]&&s_map[bo_x,bo_y-1]!=-1){bo_y=bo_y-1;continue;}
                            if (bo_x<height-1) if (s_map[bo_x+1,bo_y]<s_map[bo_x,bo_y]&&s_map[bo_x+1,bo_y]!=-1){bo_x=bo_x+1;continue;}
                            if (bo_y<width-1) if (s_map[bo_x,bo_y+1]<s_map[bo_x,bo_y]&&s_map[bo_x,bo_y+1]!=-1){bo_y=bo_y+1;continue;}
                        }
                        boolmap[bo_x,bo_y]=true;
                        s_map[x,y]=-1;
                        key[map[x,y],4]=bfs(key_x,key_y,x,y,map[x,y],boolmap);
                        //Console.WriteLine(key[map[x,y],4]);
                        //Console.WriteLine(s_key[map[x,y]]);
                    }
                        if (hp==v+key[map[x,y],4]*2+1){
                        s_map[x,y]=hp;
                        list.Add(x*width+y);
                        //Console.WriteLine(x+" "+y);
                        //Console.ReadKey();
                        return true;
                    }else return false;
                }else return false;
            }
        }else{//钥匙的情况下,与普通地面相同
            if (hp==v+1){
                s_map[x,y]=hp;
                list.Add(x*width+y);
                //Console.WriteLine(x+" "+y);
                //Console.ReadKey();
                return true;
            }else return false;
        }
    }
    static int bfs(int start_x,int start_y,int end_x,int end_y,int q,bool[,] boolmap){
        /*for (int i=0;i<height;i++){
            Console.WriteLine();
            for (int j=0;j<width;j++){
                Console.Write((boolmap[i,j]?1:0)+" ");
            }
        }*/
        //Console.ReadKey();
        int[,] bfsmap=new int[height,width];
        List<int> bfslist=new List<int>();
        bfslist.Add(start_x*width+start_y);
        bfsmap[start_x,start_y]=1;//为了图方便,直接把起点设置为1
        int step=2;
        int x=0,y=0;
        bool bo=false;
        while (true){
            for (int i=bfslist.Count-1;i>=0;i--){
                x=bfslist[i]/width;
                y=bfslist[i]%width;
                if (boolmap[x,y]) {bo=true;break;}
                if (x>0) if(bfsmap[x-1,y]==0&&map[x-1,y]!=-1) {bfsmap[x-1,y]=step;bfslist.Add((x-1)*width+y);}
                if (x<height-1) if(bfsmap[x+1,y]==0&&map[x+1,y]!=-1) {bfsmap[x+1,y]=step;bfslist.Add((x+1)*width+y);}
                if (y>0) if (bfsmap[x,y-1]==0&&map[x,y-1]!=-1) {bfsmap[x,y-1]=step;bfslist.Add(x*width+y-1);}
                if (y<width-1) if(bfsmap[x,y+1]==0&&map[x,y+1]!=-1) {bfsmap[x,y+1]=step;bfslist.Add(x*width+y+1);}
                bfslist.RemoveAt(i);
            }
            if (bo) break;
            step++;
        }
        //x=end_x;y=end_y;
        /*for (int i=0;i<height;i++){
            Console.WriteLine();
            for (int j=0;j<width;j++){
                Console.Write(bfsmap[i,j]+" ");
            }
        }*/
        //Console.WriteLine(step);
        //Console.ReadKey();
        key[map[end_x,end_y],5]=x*width+y;//钥匙插入的节点
        s_key[q]="";
        for (int i=step-1;i>1;i--){
            if (x>0) if(bfsmap[x-1,y]==i-1){x=x-1;s_key[q]+="D";continue;}
            if (y>0) if(bfsmap[x,y-1]==i-1){y=y-1;s_key[q]+="R";continue;}
            if (x<height-1) if (bfsmap[x+1,y]==i-1){x=x+1;s_key[q]+="U";continue;}
            if (y<width-1) if (bfsmap[x,y+1]==i-1){y=y+1;s_key[q]+="L";continue;}
        }
        //Console.WriteLine(s_key[q]+" "+q);
        //Console.ReadKey();
        return(step-2);
    }
    static void getinput(){
        string str=Console.ReadLine();
        string[] nums = str.Split(‘ ‘);
        height=int.Parse(nums[0]);
        width=int.Parse(nums[1]);
        key_num=int.Parse(nums[2]);
        map=new int[height,width];
        s_map=new int[height,width];
        key=new int[key_num+1,6];
        s_key=new string[key_num+1];
        for (int i=0;i<height;i++){
            str=Console.ReadLine();
            for (int j=0;j<width;j++){
                if (‘#‘==str[j]) map[i,j]=-1;
                else if (‘.‘==str[j]) map[i,j]=0;
                else{
                    map[i,j]=(int)str[j]-(int)‘A‘+1;
                    if (map[i,j]<30){//大写字母表示刺
                        key[map[i,j],2]=i;
                        key[map[i,j],3]=j;
                    }else{
                        key[map[i,j]-32,0]=i;//大小写差32,小写字母表示钥匙
                        key[map[i,j]-32,1]=j;
                    }
                }
                s_map[i,j]=-1;//用最大数充填步数计数
            }
        }
        str=Console.ReadLine();
        nums = str.Split(‘ ‘);
        start_y=int.Parse(nums[0])-1;
        start_x=int.Parse(nums[1])-1;
        str=Console.ReadLine();
        nums = str.Split(‘ ‘);
        end_y=int.Parse(nums[0])-1;
        end_x=int.Parse(nums[1])-1;
    }
    static void output(){
        for (int i=0;i<height;i++){
            for (int j=0;j<width;j++){
                Console.Write(map[i,j]+" ");
            }
            Console.WriteLine();
        }
        for (int i=1;i<key_num+1;i++){
            Console.Write(key[i,0]+" "+key[i,1]+" "+key[i,2]+" "+key[i,3]+" ");
        }
    }
}

然后附一张提交结果图,第十六位那个就是我啦~可以看到目前最优的算法应该是总和4000步

那么有什么样的情况没考虑到呢?

一个是我的程序没有把算法完全实现,在遇到这样的情况时:

...ab

.####

A####

B....

从左上走到右下,我的实现里,取钥匙b的时候会重新回到起点,而不是走到a,这样就平白多走了四步,这个优化起来应该不难。

另一种错误数据:

5 4 2
.B.a
.###
...b
.###
A...
1 1
4 5

也是从左上走到右下,最优的路径应该是:起点-->b-->B-->a-->A-->终点

但是我这个程序跑出来的结果会强行穿过A,因为在A上面一格会被计数为3,程序在判断过A的时候只需要10点Hp,而实际上却需要20点Hp。判断过A时最初是一直没找到钥匙,在找到钥匙之后hp却过大了,而hp是递增的,永远不会给用钥匙过A的机会(顺便一提就算给了机会结果也是错的)。

出现这个错误的原因是从A返回起点模拟路径失败了,现在我也没想到什么妥善的解决办法,或许在从a开始寻路的时候,如果路径碰到了刺,就需要额外进行判断...不过这样写起来很麻烦,目前也没有这么多时间琢磨这个...如果看到这篇文章的大牛有什么更好的算法的话,欢迎在评论区留言探讨~

时间: 2024-11-09 09:23:30

一道广搜寻路题的相关文章

杭电 1372 Knight Moves(广搜模板题)

http://acm.hdu.edu.cn/showproblem.php?pid=1372 Knight Moves Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6439    Accepted Submission(s): 3886 Problem Description A friend of you is doing res

hdu 1180(广搜好题)

诡异的楼梯 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 12487    Accepted Submission(s): 3120 Problem Description Hogwarts正式开学以后,Harry发现在Hogwarts里,某些楼梯并不是静止不动的,相反,他们每隔一分钟就变动一次方向. 比 如下面的例子里,一开始楼梯在

广搜简单题

Catch That Cow 题目传送:POJ - 3278 - Catch That Cow 题解:点击即传送 迷宫问题 题目传送:POJ - 3984 - 迷宫问题 DFS也可以,见另一个题解 AC代码(BFS): #include <map> #include <set> #include <cmath> #include <deque> #include <queue> #include <stack> #include &l

广搜模板题马的遍历题解

题目描述 有一个n*m的棋盘(1<n,m<=400),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步 输入格式 一行四个数据,棋盘的大小和马的坐标 输出格式 一个n*m的矩阵,代表马到达某个点最少要走几步(左对齐,宽5格,不能到达则输出-1) 本题思路是利用队列存储,将当前点可到达的下一个点入队以保证步数最小 #include<bits/stdc++.h> using namespace std; int n; int a,b,c,d,k[1001][1001]

poj 3984:迷宫问题(广搜,入门题)

迷宫问题 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7635   Accepted: 4474 Description 定义一个二维数组: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; 它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要

poj1426--Find The Multiple(广搜,智商题)

Find The Multiple Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 18527   Accepted: 7490   Special Judge Description Given a positive integer n, write a program to find out a nonzero multiple m of n whose decimal representation contains

迷宫广搜

上学期学了C,这学期学C++.感觉最难的还是算法,上周作业的一道广搜题是我第一次接触广搜,由于第一学期刚学编程就接触的太多的算法难题,不禁对代码产生畏惧,不过还好没有放弃,虽然算法很难,但我慢慢找到了一点学数学时的乐趣.先介绍一下这道未来的我看过来会觉得很简单一道题吧 You are provided a maze(迷宫), and you need to program to find the least steps to walk from the start to the end.And

HDU 2267 How Many People Can Survive(广搜,简单)

题目 //一道简单的广搜水题 #include<queue> #include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> using namespace std; struct tt { int x,y; }; char mp[310][310]; int vis[310][310]; //看了题解,发现只有4个方向,而不是8个方向....题目貌似都没说清楚

poj 3278 Catch That Cow(广搜)

Catch That Cow Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 45087   Accepted: 14116 Description Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,00