C++实践笔记(一)----迷宫算法初探
前些天在园子里看到一篇关于"脱离地牢"算法的文章,看了题目就没有在往下看了.很想自己搞定它.
如果你感兴趣,不妨看完下面的题目,就把它复制粘贴下来,一个人琢磨,可以看C++语言的工具书,但是不要去看数据结构和算法的书,就只是一个人思考,收获应该会不小哦.
脱离地牢
题目描述:
在一个神秘的国度里,年轻的王子Paris与美丽的公主Helen在一起过着幸福的生活。他们都随身带有一块带磁性的阴阳魔法石,身居地狱的魔王Satan早就想着得到这两块石头了,只要把它们溶化,Satan就能吸收其精华大增自己的魔力。于是有一天他趁二人不留意,把他们带到了自己的地牢,分别困在了不同的地方。然后Satan念起了咒语,准备炼狱,界时二人都将葬身于这地牢里。
危险!Paris与Helen都知道了Satan的意图,他们要怎样才能打败魔王,脱离地牢呢?Paris想起了父王临终前给他的备忘本,原来他早已料到了Satan的野心,他告诉Paris只要把两块魔法石合在一起,念出咒语,它们便会放出无限的光荣,杀死魔王,脱离地牢,而且本子上还附下了地牢的地图,Paris从中了解到了Helen的位置所在。于是他决定首先要找到Helen,但是他发现这个地牢很奇怪,它会增强二人魔法石所带的磁力大小,而且会改变磁力的方向。这就是说,每当Paris向南走一步,Helen有可能会被石头吸引向北走一步。而这个地狱布满了岩石与熔浆,Paris必须十分小心,不仅他不能走到岩石或熔浆上,而且由于他行走一步,Helen的位置也会改变,如果Helen碰到岩石上,那么她将停留在原地,但如果Helen移动到了熔浆上,那么她将死去,Paris就找不到她了。
Paris仔细分析了地图,他找出了一条最快的行走方案,最终与Helen相聚。他们一起念出了咒语“·#¥%^…*&@!”,轰隆一声,地牢塌陷了,他们又重见光明…
输入描述(escape.in)
输入数据第一行为两个整数n,m(3<=n,m<=20),表示地牢的大小,n行m列。接下来n行,每行m个字符,描述了地牢的地图,“.”代表通路,“#”代表岩石,“!”代表熔浆,“H”表示Helen,“P”表示Paris。输入保证地牢是封闭的,即四周均是岩石或熔浆。接下来一行有四个字符“N”(北),“S”(南),“W”(西),“E”(东)的排列,表示Paris分别向NSWE四个方向走时Helen受磁石磁力影响的移动方向。
输出描述(escape.out)
输出文件只有一行,如果Paris能找到Helen,输出一整数d,为Paris最少需要行走的步数;如果Paris在255步之后仍找不到Helen,则输出“Impossible”。注意相遇是指Paris与Helen最终到达同一个格子,或者二人在相邻两格移动后碰到了一起,而后者的步数算他们移动后的步数。
这个问题看起来很复杂,对,我觉得太复杂了,一时解决不了.于是我决定从简单的开始,我假设 Paris和Helen身上没有带着魔法石,那么呢Helen停在原地不动等着Paris来找她就成.Paris在地图里也知道了Helen的位置,那么他就得找到一条最短的路.
因为Helen不动,我干脆把熔浆神马的也去掉了.那么0表示岩石,1表示通路.还是返回最少需要行走的步数,这下就简单了...
为了使文章短一些,我把所有检查输入的语句全去掉了,不要有错误输入哦:)
首先我觉得应该定义一个类,表示地图上的每个点,点得有坐标,还要有一个标志表示这个点走不走得通.所以先给它定义三个公共成员:
int x;//第x行
int y;//第y列
注意这里的(x,y)表示第x行第y列,所以最小是1.坐标系的方向不是一般坐标轴的方向,这里的x轴向下,而y轴向右.
然后怎么办?想想怎么走才是最短呢? 要保证走的路最短,应该满足这样的条件:前进过程中到达每个点所走的步数都是最少的.怎样达成这个条件呢.两点,第一,要多条路同时试着走,不能一条路走了不通再返回走别的路;第二,走到一个点,这个点就被占用了,不能再通过别的路走到这个点了.所以要给这个类加几个成员,首先增加一个step,表示到这一点时一共走了几步.还有增加两个指针,一个指针把一条路上的点连接起来,另一个指针把花费步数相同的所有点连接起来,这样一来就可以同时走好多条路了.
Path *bp;//兄弟指针,通过它连在一起的点所花费的步数(step)都相同.
int step;//表示来到这里花了多少步
int flag;//0表示岩石,1表示通路,2表示已经来过这个点了.
再给这个类弄两个构造函数,一个是建立头指针用的,一个可以用来写进坐标和flag.
Path(int i,int j,int f):fp(0),bp(0),step(0)//构造函数,写入点的坐标(x,y),以及标志(flag)
{
x=i,y=j;
flag=f;
}
设定几个全局变量.m,n表示迷宫的行数和列数(ex,ey)表示终点的坐标.
另外定义一个指向Path型对象的头指针,用来把step值相同的点连起来.这很重要.
#include <iostream>
#include <vector>
#include <string>
#include <math.h>
#include "Path.h"
using namespace std;
int m=0,n=0,ex=0,ey=0;
Path *p1=new Path();
这样的话,就可以先写输入部分的代码:
cout<<"请输入两个大于2的数字,分别表示迷宫的行数和列数,用空格隔开:"<<flush;
cin>>m>>n;
vector<vector<Path> > ce(m+1,vector<Path>(n+1));
cout<<"请输入每个格子的属性(0表示岩石,1表示可以通行),输入完第一行再输入第二行,以此类推.用空格隔开."<<endl
<<"你需要输入"<<m<<"乘以"<<n<<"等于"<<m*n<<"个数字:"<<endl;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
int f;
cin>>f;
ce[i][j]=Path(i,j,f);
}
int bx,by;
cout<<"请输入四个大于0的数字,分别表示起点的坐标和终点的坐标,用空格隔开:"<<flush;
cin>>bx>>by>>ex>>ey;
if(bx==ex&&by==ey)//同一点
{
cout<<"起点和终点在一起,还走什么走..."<<endl;
system("PAUSE");
return -1;
}
if(!ce[bx][by].flag||!ce[ex][ey].flag)//起点或终点在岩石上.
{
cout<<"杯具,起点或终点在岩石上..."<<endl;
system("PAUSE");
return -1;
}
由于是变长二维数组,我使用了vector容器,感觉容器用起来比较方便.(其它的我也不会...)而且为了代码更直观,我忽略了0下标.
另外我还定义了两个函数,一个是内联函数,可以得到两点之间的距离.
{
return abs(x-ex)+abs(y-ey);
}
还有一个是Path的一个成员函数,用来标记来过的点(step=2),同时记录下到达这一点时的步数.最重要的是把每条路连起来同时把step值相同的点连起来.
{
flag=2;//表示这个点已经来过了
fp=p;
step=p->step+1;//step加1
if(p1->bp!=p)//如果p1->bp==p,就说明现在开始用p1连接所有step=this->step的点;
//如果p1->bp!=p,就说明正连着呢,只管往头指针后面插入就成.
this->bp=p1->bp;
p1->bp=this;//这是必须的.
}
下面是我写的找路的算法,这个算法其实就是先找出所有走1步能到的点,再找走2步能到的点,再找走3步能到的点......就这么找下去...什么时候找到终点了就到了.
p1->bp=p;
int x=bx,y=by;//当前的坐标,vector的下标
ce[bx][by].flag=2;//起点已经来过了
while(p)
{
if(dis(x,y,ex,ey)==1)//到终点了.
{
cout<<"需要走"<<p->step+1<<"步能到目的地."<<endl;
system("PAUSE");
return 0;
}
if(y>1&&ce[x][y-1].flag==1)//往西
ce[x][y-1].add_flag(p,p1);
if(y<n&&ce[x][y+1].flag==1)//往东
ce[x][y+1].add_flag(p,p1);
if(x>1&&ce[x-1][y].flag==1)//往北
ce[x-1][y].add_flag(p,p1);
if(x<m&&ce[x+1][y].flag==1)//往南
ce[x+1][y].add_flag(p,p1);
if(p->bp)//再从p的兄弟节点出发
{
p=p->bp;
x=p->x,y=p->y;
}
else if(p!=p1->bp)//兄弟节点走完了,从下一步的一个候选点开始
{
p=p1->bp;
x=p->x,y=p->y;
}
else//兄弟节点也没有,也没有下一步能到的点了,那是真没办法了..
{
cout<<"实在是到不了终点啊!"<<endl;
system("PAUSE");
return -1;
}
}
return 0;//不会到这来的.
这样一来,这个Helen不动版本的"脱离地牢"就算是解决了.那么,先再学两章C++,再让Helen动起来~~~(也已经解决了,看方法点这里)
Fighting!