城市地图和最少转机问题

文章通过城市地图和最少转机问题的例子,阐述了深度搜索与广度搜索在解决图的最短路径问题上的差异。深度搜索适用于有向图,而广度搜索在所有边权值相等的情况下更为合适。在城市地图问题中,深度搜索找到从1号城市到5号城市的最短路径为9公里。在最少转机问题中,广度搜索用于找到从1号城市到5号城市转机次数最少的路径,即2次。
摘要由CSDN通过智能技术生成

使用深度搜索和广度搜索,来遍历图,将会得到——这个图的生成树,利用这个可以解决实际问题,如城市地图和转机问题。

城市地图

【题目描述】

暑假小哼想到小哈家里去玩,小哼和小哈住在不同的城市,并且小哼之前从来没有去过小哈家,这是小哼第一次上门。怎么去呢?小哼便想起了百度地图。百度地图一下子给出了从小哼家到小哈家的最短行车方案。爱思考的小哼想知道百度地图是如何计算出最短行车方案的。求出从小哼家到小哈家的最短路径。

【输入】

第一行两个数字,分别为 n 和 m,n 代表小哼家到小哈家及其中间的城市总数,m 代表这些城市之间有多少条公路。接下来的 m 行每行是类似 “ a b c ”这样的数据,表示有一条路可以从城市 a 到城市 b,并且路程为 c 公里,但是不代表城市 b 可以到达城市 a。

【输出】

一个数字,代表从小哼家到小哈家的最短路径。

【样例输入】

5 8
1 2 2
1 5 10
2 3 3
2 5 7
3 1 4
3 4 4
4 5 5
5 3 3

【样例输出】

9

解题思路

把样例输入的数据转化为容易理解的图形:

然后从图可以完善邻接矩阵,二维数组中第 i 行第 j 列表示的是顶点 i 到顶点 j 是否右边。∞ 表示没有边,非 ∞ 的数表示有边,将自己到自己(即 i 等于 j)为 0。如下图:

 该邻接矩阵表示了任意两个城市之前的路程,比如 e[1][2] 的值为 2 就表示从 1 号城市到 2 号城市的路程为 2 公里,而 e[1][3] 的值为 ∞,表示目前从 1 号城市不能到达 3 号城市。

用深度搜索解题是这样:

根据邻接矩阵,从 1 号城市出发,可以到达 2 号城市和 5 号城市;

1->2  其中如果先到 2 号城市,从 2 号城市出发,可以到达 3 号城市和 5 号城市;

1->2->3  其中然后先到 3 号城市,从 3 号城市出发,可以到达 4 号城市;

1->2->3->4->5  到达 4 号城市之后可以到达 5 号城市,此时总路程为 14.

接着找另一种方案:

首先退回 4 号城市,但是除了可以到达 5 号城市之外,没有其他路可走;

然后退回 3 号城市,除了可以到达 4 号城市之外,也没有其他路可走;

接着退回 2 号城市,还有一条路可以到达 5 号城市,路径为 1->2->5,路程为 9.

此时最短路径更新为 9。

再找其他方案:

从 2 号城市退回 1 号城市,有一条路直达 5 号城市,路径为 1->5,路程为 10.

代码如下:

#include<stdio.h>
int n,m,book[100],e[100][100];//定义标记数组和二维邻接矩阵 
int min=99999999;//这里假设最大数为 99999999 
//第一个数代表经过的城市,第二个数代表由当前方案到城市 t时走过的路径 
void fun(int t,int sum)
{
	int i;
	if(sum>min)//如果走到当前城市,其路程已经大于先前找到的方案中的最短路径了,就可以结束当前方案 
	return ;
	if(t==n&&min>sum)//如果找到小哈所在城市,判断其路径是否比之前找到的最短路径小 
	min=sum;
	for(i=1;i<=n;i++)//把每个城市遍历一遍 
	{
		if(book[i]==0&&e[t][i]!=99999999)//当前方案先前不能访问过该点,且两个城市之间要可以走 
		{
			book[i]=1;//满足条件则标记 
			fun(i,sum+e[t][i]);
			book[i]=0;//一个方案结束后,要取消标记 
		}
	}
	return ;
}
int main()
{
	int a,b,c,i,j;
	scanf("%d %d",&n,&m);//读入城市数和路线数 
	for(i=1;i<=n;i++)//初始化二维邻接矩阵 
	{
		for(j=1;j<=n;j++)
	    {
		    if(i==j)//自己到自己就把值赋为 0,其实不赋为 0也行,想一想为什么 
		    e[i][j]=0;
		    else//其他情况赋为最大值 
		    e[i][j]=99999999;
	    }
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d %d %d",&a,&b,&c);//把读入的信息转化到二维数组 
		e[a][b]=c;
	}
	book[1]=1;//从小哼家的一号城市出发,进入函数前标记起点 
	fun(1,0);//从起点进入函数 
	printf("%d\n",min);//输出最短路径 
	return 0;
}

这是用深度搜索解题的例子,对于这个题目,一般实际情况下,道路都是可以双向到达的,而该题是有向图,当我们改为无向图时,相应的邻接矩阵改为:

这样修改之后,代码实现几乎没有很大改变,只有初始化时要增加一个步骤:

    for(i=1;i<=m;i++)
	{
		scanf("%d %d %d",&a,&b,&c);//把读入的信息转化到二维数组 
		e[a][b]=c;
		e[b][a]=c;//新增的步骤 
	}

要是用广度搜索,要定义一个结构体,包括当前走过的城市编号和走的路程,不过跟深度搜索一样,需要找到最短路程,当队列第一次找到目标城市后,还不能结束寻找下一个方案,因为只找到了从出发城市到目标城市走过的最少路的条数,还未找到最短路径。

最少转机

【题目描述】

小哼和小哈一同坐飞机去旅游,他们现在位于 1 号城市,目标是 5 号城市,可是 1 号城市到 5 号城市没有直航。不过小哼收集了很多的航班信息,现在小哼希望找到一种乘坐方式,使得转机次数最少,如何解决呢?

【输入】

第一行输入四个数字,分别为 n,m,a,b,分别代表城市数量、航班数量、出发城市、目标城市。

接下来的 m 行是类似 “ a b ”这样的数据表示城市 a 和城市 b 之间有航线,城市 a 和城市 b 之间可以相互到达。

【输出】

输出一个数字,表示最少转机次数。

【样例输入】

5 7 1 5
1 2
1 3
2 3
2 4
3 4
3 5
4 5

【样例输出】

2

解题思路 

这个题用广度搜索更加方便,首先定义一个结构体,存储当前到达的城市编号和转机次数。

首先将 1 号城市入队,通过 1 号城市我们可以到达 2 号和 3 号城市。2 号城市又可以扩展出 3 号城市和 4 号城市。但是 3 号城市已经在队列中,所以只需将 4 号城市入队。接下来 3 号城市又可以扩展到 4 号城市和 5 号城市,因为 4 号城市也已经在队列中,所以将 5 号城市入队。此时,找到了目标城市,结束扩展。

因为最先找到 5 号了,说明转机次数已经是最少的,如果要接着找,接下来找的转机次数只会大于等于先前的。

代码如下:

#include<stdio.h>
struct node//定义结构体,存储到当前城市的编号和转机次数 
{
	int x;
	int sum;
};
int book[100],e[100][100];//定义标记数组和二维邻接矩阵 
int main()
{
	struct node k[10000];
	int n,m,start,end,a,b,i,j;
	scanf("%d %d %d %d",&n,&m,&start,&end);//输入城市和航班个数,起始城市和目标城市 
	for(i=0;i<n;i++)//初始化邻接矩阵 
	{
		for(j=0;j<n;j++)
		{
			if(i==j)//自己到自己的情况赋为 0 
			e[i][j]=0;
			else//其他情况默认为不能到达 
			e[i][j]=99999999;
		}
	}
	for(i=0;i<m;i++)
	{
		scanf("%d %d",&a,&b);
		e[a][b]=1;//把能够两个城市能够相互到达的,标记为 1 
		e[b][a]=1;
	}
	int head=1,tail=1;//定义队列 
	k[tail].x=start;//起始城市入列 
	k[tail].sum=0;
	tail++;
	book[start]=1;//把起始城市标记为 1 
	while(head<tail)//当队列不为空时才进行循环 
	{
		int flag=1;
		for(i=0;i<n;i++)//遍历每一个城市 
		{
			//满足为被标记且两座城市之间有航班则入队 
			if(book[i]==0&&e[k[head].x][i]==1)
			{
				book[i]=1;//入队前标记 
				k[tail].x=i;
				k[tail].sum=k[head].sum+1; 
				tail++;
			}
			if(k[tail-1].x==end)//如果已经找到目标城市,则结束循环 
			{
				flag=0;//当前的 break只结束内层循环,定义一个 flag做标记 
				break;
			}
		}
		if(flag==0)//如果 flag被标记为 0,结束循环 
		break;
		head++;//一个点扩展完之后,接着扩展下一个点 
	}
	printf("%d\n",k[tail-1].sum);//输出最先找到的转机次数,就是最少转机次数 
	return 0;
} 

总结

虽然很多题目,深度搜索和广度搜索都适用,但是像上面两个例子,分别更适合深度搜索和广度搜索,广度搜索更适用于所有边权值相同的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明里灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值