【数据结构机试复习8】 最短路径1 & 最短路径 2

题目名称 : 最短路径1

时间限制 : 1000 ms


空间限制 : 32 MB


题目描述

给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距

离有多条路线,则输出花费最少的。


输入描述:
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点t。n和m为0时输入结束。 (1<n<=1000, 0<m<100000, s != t)


输出描述:
输出 一行有两个数, 最短距离及其花费。


输入
3 2
1 2 5 6
2 3 4 5
1 3
0 0

输出

9 11


个人觉得这道题还挺复杂的,要考虑两个权重——距离、花费;

点数、边数的上限值n、m也很大;

采用Dijkstra(迪杰斯特拉)算法。

#include <iostream>
#include <limits>
#define Vmax 1001
using namespace std;
int inf = numeric_limits<int>::max(); //相当于无穷

int lenth[Vmax][Vmax]; //边的长度邻接矩阵
int value[Vmax][Vmax]; //边的权重邻接矩阵

void shortestPath(int start, int end, int Vnum) {
	int dist[Vmax]; //s到i的最短路径长度记为dist[i]
	int cost[Vmax]; //s到i的最短路径花费记为cost[i]
	bool v[Vmax]; //记录已求得最短路径的顶点集
	int i, j;
	for (i = 1; i <= Vnum; i++) {
		dist[i] = lenth[start][i]; //最短距离先设为直接距离
		cost[i] = value[start][i];
		v[i] = false; //均未求得最短路径
	}
	v[start] = true;
	//
	for (i = 1; i <= Vnum; i++) {
		int minDist = inf;
		int index = 0; //寻找与s距离最近的邻点
		for (j = 1; j <= Vnum; j++) {
			if (!v[j] && dist[j]<minDist) {
				minDist = dist[j];
				index = j;
			}
		}
		v[index] = true; //将最近的邻点加入集合v中

		for (j = 1; j <= Vnum; j++) {
			if (!v[j] && dist[index] + lenth[index][j]<dist[j]) { //j不在v中且以index为中转的路径更短
				dist[j] = dist[index] + lenth[index][j];
				cost[j] = cost[index] + value[index][j];
			}
			else if (!v[j] && dist[index] + lenth[index][j] == dist[j] && cost[index] + value[index][j]<cost[j]) {
				//路径一样但花费更少
				cost[j] = cost[index] + value[index][j];
			}
		}
	}
	cout << dist[end] << " " << cost[end];
}

int main() {
	int n, m, a, b, d, p, s, t;
	int i, j;
	while (cin >> n >> m, m || n) {
		for (i = 1; i <= n; i++) {  //初始化邻接矩阵
			for (j = 1; j <= n; j++) {
				if (i == j) lenth[i][j] = 0;
				else lenth[i][j] = inf;
			}
		}
		for (i = 0; i<m; i++) { //读入数据
			cin >> a >> b >> d >> p;
			lenth[a][b] = lenth[b][a] = d;
			value[a][b] = value[b][a] = p;
		}
		cin >> s >> t;
		shortestPath(s, t, n);
	}
	return 0;
}


Dijkstra(迪杰斯特拉)算法的核心思想是:  按路径长度递增的次序产生最短路径

        假设图中有n个点,a为起点,用数组dist[n]来存放当前所找到a到每个终点的最短路径。

如,dist[i]表示当前a到i的最短路径长度。

        初始时,dist[i]的值就为a、i之间的直接距离(如存在边,则为边的权重,否则为+∞);

        在dist[n]中,找到最小值dist[k],这就是从a出发的长度最短的一条最短路径。

        那么下一条长度次短的最短路径是哪一条呢?

        假设V为以求得最短路径的终点的集合(程序中用bool数组v,v[i]表示某点i是否在V中),可以证明:

下一条最短路径(设其终点为x),只能是弧<s,x>,或者是中间只经过V中的顶点而到达x的路径。

反证法:假设下一条最短路径为(s,t,x),其中t不在V中,那么由于(s,t)必然小于(s,t,x),所以下一条最短的路径就不会是(s,t,x),因为至少(s,t)比它更短。

所以算法过程描述如下:

        设s为起点,图的点集为E,集合V为已求得最短路径长度的终点

        (1)对于不属于V的点,找到dist中的最小值dist[k],将k加入V中

        (2)以k为中转点,更新s到还未加入V中的点的最短路径长度。

         重复(1)、(2),直到所有的点加入集合V。



题目名称 : 最短路径2

时间限制 : 1000 ms

空间限制 : 32 MB

题目描述
N个城市,标号从0到N-1,M条道路,第K条道路(K从0开始)的长度为2^K,求编号为0的城市到其他城市的最短距离

输入描述:
第一行两个正整数N(2<=N<=100)M(M<=500),表示有N个城市,M条道路 

接下来M行两个整数,表示相连的两个城市的编号

输出描述:
N-1行,表示0号城市到其他城市的最短路,如果无法到达,输出-1。

输入
4 4 

1 2 

2 3 

1 3 

0 1



输出

11


不得不吐槽一下,TA在matrix上给的测试样例真的很弱= =,已经查出三道题写得有问题但是在matrix上都通过了,包括这道题,想想如果M达到500的话,2^500将是一个非常巨大的数字,int肯定会溢出的,可是当时并没有处理,为了偷懒,直接Floyd吧,这道题应该也不会考吧(ಡωಡ) 

#include<iostream>
#include <math.h>
using namespace std;

int path[105][105];
int main() {
	int n, m,x,y;
	while (cin >> n >> m) {
		memset(path, -1, sizeof(path));
		for (int i = 0; i < m; i++) {
			cin >> x >> y;
			path[x][y] = path[y][x] = pow(2, i);
		}
		for(int k=0;k<n;k++)
			for(int i=0;i<n;i++)
				for (int j = 0; j < n; j++) {
					if (path[i][k] == -1 || path[k][j] == -1) continue;
					if (path[i][k] + path[k][j] < path[i][j] || path[i][j]==-1)
						path[i][j] = path[i][k] + path[k][j];				
				}
		for (int i = 1; i < n; i++)
			cout << path[0][i] << endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值