TOJ 1005


题目连接:

http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1005


题目分类:

搜索 - 最优路径


数据结构:

struct NODE{
	int x,y                  //坐标
	int H;                   //启发式估计 值等于与终点横纵坐标差的和
	int STEP;                //步数 
}
deque _OPEN,_CLOSE       //开表,关表队列
NODE [][]                //地图


思路分析:

--------------------------------------------------------------------------------------------

A* 算法:
用于求得最短寻路问题
从起点开始,每一步都需要依靠启发式函数来估计接下去需要走的路.
这样可以最大限度的跳过没有必要的路.
相对于广度搜索,它的效率十分的高.几乎所有的路线都是贴最近的路走.
将起点列入 开启列表中,它表明了那些点曾经被访问过.也就是那些待考证的点.
因为列表中暂时唯一只有起点,所以将它拿出来,列入 关闭列表 ,代表已经访问过,不必在访问.
围绕刚拿出来的点,考察它周围所有的点,记录下H值和从该点到周围点的步数(+1).
并一一放入 开启列表 里.
将开启列表按照H值从小到大排序,因为我们暂且需要更有希望近的点.虽然还未真正的知道.
讲H值最小(也就是最近)的点同刚开始的起点一样,从 开启列表 中取出,放入关闭列表.
重复上述步骤.
一旦发现刚才的选择是一条死路,也不要紧.
因为前面的途中都讲有可能的点(尽管希望没有选择的那条路大)都保存在开启列表里.
只需从开启列表中,选择最近 第二有希望的点继续拓展.
因为之前记录过步数(代表从拓展点走到另一个点的步数),所以不会发现绕路的现象.
每个点都是由之前的点 "直径" 走来,相当于它的父节点
这样就可以略去 死路走过的节点.
如此下去,直到找到终点
或者开启列表中已经没有任何节点,代表不可能由起点走向终点.



所以,实际上"死路"那部分的路径是探测过的,而且实实在在跟正确路径一样保存在关闭列表里.

只是题目只求最短步数,不求实际路径.则可以借由记录STEP步数来忽略那部分走的路.

证明:
简略的说,
从起点开始假设能搜到N个周围的点(上下左右),
代表有N条可能的希望.并保存在开启列表中
假设
沿着某一条最有希望的点,
该点最后没有任何可以搜索的点,则表明该点周围已经没有路
则可以选择当初在开启列表中第二有希望的点继续探查.
相当于有足够数量的人走迷宫
每个人拿着计步器
当看见分叉时,由一个人带着计步器选择最有希望的路
另外个人在原地等待.
一旦发现那条路行不通,则有第二个人继续走第二有希望的路线.
以此类推.
假设有那么一条路径通向终点,
则必定是最优路线.
因为比它有希望的路线或已被证明是死路
假设所有的路都是死路,
则可以证明在起点的时候选择都是 未来没有希望的路.
所以可以证明,必能选择一条正确的路
要么是最优路线,要么可以得出所有的路都不行


源代码:

#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;

struct NODE
{
	char C;
	int x,y;
	int H;  								// 曼哈顿估算  因为只能上下左右,所以G(步长)值无意义
	int step;
	bool operator < (const NODE & k) const{      				//重载比较运算符
		return H < k.H;
	}
	bool operator == (const NODE & k) const{
		return x == k.x && y == k.y;
	}
};

/* 寻找节点 n 是否存在于队列 ns 中 */
deque<NODE>::iterator isfinded(NODE n,deque<NODE> &ns)
{
	deque<NODE>::iterator nsp=ns.begin();
	while(nsp!=ns.end())
	{
		if(*nsp==n) return nsp;
		nsp++;
	}
	return ns.end();
}

int AStar(NODE map[21][21],NODE start,NODE end,int n,int m)
{
	NODE CNODE;
	deque<NODE> _OPEN,_CLOSE;
	deque<NODE>::iterator _TMPO,_TMPC;
	start.step=0;  										//记起点节点步数为0 
	_OPEN.push_back(start);
	
	while(_OPEN.size()!=0)
	{
		sort(_OPEN.begin(),_OPEN.end());						//按H值进行升序排序
		CNODE=_OPEN.front();
		_OPEN.pop_front();
		_CLOSE.push_back(CNODE);
		
		if(CNODE==end)
		{
			return CNODE.step;							//返回总步数 
		}
		else
		{
			for(int i=-1;i<=1;i++)
			for(int j=-1;j<=1;j++)
				// 判断i,j其一为0且不都为0 (只能选上下左右,且不能选自身格子) 另外周围的格子不能越界 
				if( (i==0 || j==0 ) && ( i!=0 || j!=0 ) && CNODE.x+i<n && CNODE.y+j<m && CNODE.x+i>=0 && CNODE.y+j>=0)
				{
					NODE newNode=map[CNODE.x+i][CNODE.y+j],tmp;	//建立新临时节点 
					_TMPO=isfinded(newNode,_OPEN); 				//是否在OPEN表中 
					_TMPC=isfinded(newNode,_CLOSE);				//是否在CLOSE表中 
					
					if(_TMPO==_OPEN.end() && _TMPC==_CLOSE.end())//新节点同时不在两张表中 
						if(newNode.C!='*')						 //且不为障碍物 
						{
							newNode.H= ( abs( end.x-newNode.x )+abs( end.y-newNode.y) )*10;
							newNode.step=CNODE.step+1;			 //记下步数 
							_OPEN.push_front(newNode);
						}
				}
		}
	}
	return 0;
}

int main()
{
	int i,j,N,M,T,path;
	NODE tower[21][21],start,end;
	
	while(scanf("%d%d%d",&N,&M,&T) && ( N || M || T))
	{
		for(i=0;i<N;i++)
		{
			getchar();
			for(j=0;j<M;j++)
			{
				scanf("%c",&tower[i][j].C);
				tower[i][j].x=i; tower[i][j].y=j;
				if(tower[i][j].C=='S') 
					start=tower[i][j];
				if(tower[i][j].C=='P') 
					end=tower[i][j];
			}
		}
		
		path=AStar(tower,start,end,N,M);
		
		if(path<=T && path>0)
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值