信息学奥赛一本通 广度优先搜索 例8.1解释

题目:

如图,表示的是从A城市到H城市的交通图。从图中可以看出,从A城市到H城市要经过若干个城市。先要找出一条经过城市最少的一条路线。(由A城市到H城市)
由于图片上传问题我们用下表表示每个地点间的关系:

ABCDEFGH
A10001011
B01111011
C01100111
D01011101
E11011100
F00111110
G11100110
H11110001

‘1’表示连通,‘0’表示不连通。

分析:

对于这个问题我们首先应考虑几个小问题:

  1. 如何记录数据?
  2. 如何遍历?
  3. 如何输出?
如何记录数据?

首先我们需要建立一个数组a来存放每一个节点的内容,再建立一个数组b来指向对应a的母节点。同时,为了防止陷入死循环,我们应再建立一个标记数组vis来标记已经访问的对象。最后,在建立一个数组lt来判断每个城市之间的连通性。

如何遍历?

对于这个问题我们采取广度优先的算法,从A节点开始建立树,逐层拓展,直到某一层第一次出现H。此情况由于遍历的层数最少,故一定是最优解。所以我们需要准备两个变量,一个为head,为我们正在遍历的母节点,而tail为此时正在建立的子节点。可以想到,同一层的所有节点一定位于相邻的一组数字中,而head依次增大,来实现同层母节点的依次拓展,进而实现广度优先搜索,tail依次增大来确保下一层节点位于相邻的一组数字中。在拓展子节点内容的同时,不要忘记添加母节点的指向关系。当然,对于同一个母节点我们要依次考虑它和其他所有城市的连通关系和是否满足未被使用的条件。在满足这些条件的基础上建立子节点记录内容并标记已被使用。并注意此处的标记不需要取消:因为题目要求求最优解,最高的一层到达目的地的解即为所求解,而在标记之后,后面的节点如果访问到被标记的这个位置,这个节点一定在标记时的母节点的同层或下一层,由于同一个位置的生成树是一样的。所以,在后面的点访问得到的解一定不是最优解,故没有必要再次访问。因此无需解除标记。以tail大于head为循环条件,当遍历的最终解的时候,跳出循环,即令head等于tail。注意在广度优先搜索之前对变量进行初始化,head为0是为了在循环中使母节点从a[1]开始(head++),tail为1是为了在记录数据时从a[2]开始,b[1]=0是为了在输出时方便作为输出循环的终止条件,同时记录1号位置已经被访问。

 void bfs()
 {
 	int head,tail,i;
 	head=0;tail=1;
 	a[1]=1;		//最顶层
	b[1]=0;		//指向空
	vis[1]=1; 	//访问标记
	do
	{
		head++;			//节点转移一位
		for(int i=1;i<=8;i++)
		if(!lt[head][i]&&!vis[i]) //连通且没有被访问过
		{
			tail++;		//队尾+1,用来存放内容
			a[tail]=i;	//记录内容
			b[tail]=head;	//标记其母节点
			vis[i]=0;//标记城市走过
			if(i==8)
			{
				print(tail);
				head=tail;
				break;
			 } 
			
		}
	}
	while(head<tail);	
} 
如何输出?

再找到目标位置后,根据b所指方向依次输出知道a[1]。(a[1]的指向b[1]为0,依次作为循环结束的标志)

void print(int d)
{
	cout<<char(a[d]+64);		//目标内容 
	while (b[d])
	{
		d=b[d];					//将d指向此刻a的母节点 
		cout<<"--"<<char(a[d]+64);		//输出内容 
	}
	cout<<endl;
 }

代码

#include<iostream>
using namespace std;
bool lt[9][9]={{0,0,0,0,0,0,0,0,0},
				{0,1,0,0,0,1,0,1,1},
				{0,0,1,1,1,1,0,1,1},
				{0,0,1,1,0,0,1,1,1},
				{0,0,1,0,1,1,1,0,1},
				{0,1,1,0,1,1,1,0,0},
				{0,0,0,1,1,1,1,1,0},
				{0,1,1,1,0,0,1,1,0},
				{0,1,1,1,1,0,0,0,1}};
int a[101],b[101];    //a来存放城市(存放树的内容),b来存放内容对应的母节点
bool vis[9];	     //用来记录城市是否被访问过
void print(int d)
{
	cout<<char(a[d]+64);		//目标内容 
	while (b[d])
	{
		d=b[d];					         //内容的母节点 
		cout<<"--"<<char(a[d]+64);		//输出内容 
	}
	cout<<endl;
 } 
 void bfs()
 {
 	int head,tail,i;
 	head=0;tail=1;
 	a[1]=1;		//最顶层
	b[1]=0;		//指向空
	vis[1]=1; 	//访问标记
	do
	{
		head++;			//节点转移一位
		for(int i=1;i<=8;i++)
		if(!lt[head][i]&&!vis[i]) //连通且没有被访问过
		{
			tail++;		//队尾+1,用来存放内容
			a[tail]=i;	//记录内容
			b[tail]=head;	//标记其母节点
			vis[i]=0;//标记城市走过
			if(i==8)
			{
				print(tail);
				head=tail;
				break;
			 } 
			
		}
	}
	while(head<tail);	
} 
int main()
{
	bfs();
	return 0;	
 } 

欢迎指正错误!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值