PAT (Advanced Level) Practice 1030 Travel Plan (30 分) 凌宸1642

PAT (Advanced Level) Practice 1030 Travel Plan (30 分) 凌宸1642

题目描述:

A traveler’s map gives the distances between cities along the highways, together with the cost of each highway. Now you are supposed to write a program to help a traveler to decide the shortest path between his/her starting city and the destination. If such a shortest path is not unique, you are supposed to output the one with the minimum cost, which is guaranteed to be unique.

译:旅行地图会给出城市间沿公路的距离,以及每条公路的成本。现在你应该写一个程序来帮助旅行者决定从他/她的出发城市到目的地之间的最短路径。如果这样一条最短路径不是唯一的,那么您应该输出代价最小的那条路径,它保证是唯一的。


Input Specification (输入说明):

Each input file contains one test case. Each case starts with a line containing 4 positive integers N, M, S, and D, where N (≤500) is the number of cities (and hence the cities are numbered from 0 to N−1); M is the number of highways; S and D are the starting and the destination cities, respectively. Then M lines follow, each provides the information of a highway, in the format:

City1 City2 Distance Cost

where the numbers are all integers no more than 500, and are separated by a space.

译:每个输入文件包含一个测试用例。每一种情况都以包含4个正整数N、M、S和D的一行开始,其中N(≤500)是城市的数量(因此城市的编号从0到N−1);M为公路数;S和D分别为起点城市和终点城市。接下来是M行,每一行提供一条公路的信息,格式如下:

City1 City2 Distance Cost

其中的数字都是不超过500的整数,并用空格隔开。。


Output Specification (输出说明):

For each test case, print in one line the cities along the shortest path from the starting point to the destination, followed by the total distance and the total cost of the path. The numbers must be separated by a space and there must be no extra space at the end of output…

译:对于每个测试用例,在一行中打印从起点到目的地的最短路径上的城市,然后是路径的总距离和总成本。数字之间必须用一个空格隔开,并且在输出的末尾不能有额外的空格 。


Sample Input (样例输入):

4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20

Sample Output (样例输出):

0 2 3 3 40

The Idea:

最短路径的 最小花费 问题。

The Codes:

Dijkstra + DFS
#include<bits/stdc++.h>
using namespace std ;
const int maxv = 520 ;
const int inf = 0x3fffffff ;
int G[maxv][maxv] , cost[maxv][maxv] ; // 图邻接矩阵 , 和花费矩阵
int d[maxv] ;  // 起点 到各个点的最短路径
bool vis[maxv] = { false } ; // 标记数组
int c[maxv] ;  // 最小花费 
int pre[maxv] ; // 记录结点的前驱结点 
int m , n , s , t ; // 顶点数 边数 起点 终点 
void Dijkstra(int s){
	fill(d , d + maxv , inf) ; 
	fill(c , c + maxv , inf) ; 
	for(int i = 0 ; i < n ; i ++) pre[i] = i ;
	d[s] = c[s] = 0 ;
	for(int i = 0 ; i < n ; i ++){
		int u = -1 , minv = inf ; // 找到最小的 d[u]  
		for(int j = 0 ; j < n ; j ++){
			if(vis[j] == false && d[j] < minv){
				u = j ;
				minv = d[j] ;
			}
		}
		if(u == -1) return ;
		vis[u] = true ; // 设 u 已访问 
		for(int v = 0 ; v < n ; v ++){
			if(vis[v] == false && G[u][v] != inf){
				if(d[u] + G[u][v] < d[v]){
					d[v] = d[u] + G[u][v] ;  // 优化 d[v]
					c[v] = c[u] + cost[u][v] ; // 更新花费
					pre[v] = u ; // 令 v 的前驱为 u 
				}else if(d[u] + G[u][v] == d[v]){
					if(c[u] + cost[u][v] < c[v]){
						c[v] = c[u] + cost[u][v] ; // 更新花费 
						pre[v] = u ; // 令 v 的前驱为 u 
					}
				}
			}
		}
	}
}
// 遍历最短路径
void DFS(int v){
	if(v == s){
		printf("%d " , v) ;
		return ;
	}
	DFS(pre[v]) ;
	printf("%d " , v) ;
}
int main(){
	scanf("%d%d%d%d" , &n , &m , &s , &t) ;// 顶点数 边数 起点 终点 
	fill(G[0] , G[0] + maxv * maxv , inf) ; // 初始化图 
	int u , v ; 
	for(int i = 0 ; i < m ; i ++){
		scanf("%d%d" , &u , &v) ; // 边 u -> v 
		scanf("%d%d" , &G[u][v] , &cost[u][v]) ; // 边权 花费 
		G[v][u] = G[u][v] ;
		cost[v][u] = cost[u][v] ;
	} 
	Dijkstra(s) ; 
	DFS(t) ;
	printf("%d %d\n" , d[t] , c[t]) ;
	return 0;
}

Dijkstra + DFS 更加通用的模板
#include<bits/stdc++.h>
using namespace std ;
const int maxv = 520 ;
const int inf = 0x3fffffff ;
int G[maxv][maxv] , cost[maxv][maxv] ; // 图邻接矩阵 , 和花费矩阵
int d[maxv] ;  // 起点 到各个点的最短路径
bool vis[maxv] = { false } ; // 标记数组
vector<int> pre[maxv] ; // 记录结点的前驱结点 
vector<int> path , temp ; // 最优路径, 临时路径 
int minCost = inf ; // 第二标尺最优值
int m , n , s , t ; // 顶点数 边数 起点 终点 
void Dijkstra(int s){
	fill(d , d + maxv , inf) ;
	d[s] = 0 ;
	for(int i = 0 ; i < n ; i ++){
		int u = -1 , minv = inf ; // 寻找最小的 d[u]
		for(int j = 0 ; j < n ; j ++){
			if(vis[j] == false && d[j] < minv){
				u = j ;
				minv = d[j] ;
			}
		}
		if(u == -1) return ;
		vis[u] = true ;
		for(int v = 0 ; v < n ; v ++){
			if(vis[v] == false && G[u][v] != inf){
				if(d[u] + G[u][v] < d[v]){ // 以 u 为中介点可以是 d[v] 更优
					d[v] = d[u] + G[u][v] ; // 更新 d[v]
					pre[v].clear() ; // 清空 pre[v] 
					pre[v].push_back(u) ; // 令 v 的前驱为 u
				}else if(d[u] + G[u][v] == d[v]){ // 多条最短路径
					pre[v].push_back(u) ; // 令 v 的前驱为 u
				}
			}
		}
	}
}

void DFS(int v){
	// 递归边界
	if(v == s){
		temp.push_back(v) ; // 将 起点 加入路径最后面
		int tempCost = 0 ;
		for(int i = temp.size() - 1 ; i > 0 ; i --){
			int id = temp[i] , idNext = temp[i - 1] ;
			tempCost += cost[id][idNext] ; // 增加边 id -> idNext 的边权花费 
		}
		if(tempCost < minCost){
			minCost = tempCost ; // 更新最小花费 
			path = temp ; //更新路径 
		} 
		temp.pop_back() ; // 将刚加入的结点删除
		return ;
	} 
	temp.push_back(v) ; // 将 v 加入路径
	for(int i = 0 ; i < pre[v].size() ; i ++){
		DFS(pre[v][i]) ;
	} 
	temp.pop_back() ; // 将刚加入的结点删除
} 
int main(){
	scanf("%d%d%d%d" , &n , &m , &s , &t) ;// 顶点数 边数 起点 终点 
	fill(G[0] , G[0] + maxv * maxv , inf) ; // 初始化图 
	fill(cost[0] , cost[0] + maxv * maxv , inf) ; // 初始化图 
	int u , v ; 
	for(int i = 0 ; i < m ; i ++){
		scanf("%d%d" , &u , &v) ; // 边 u -> v 
		scanf("%d%d" , &G[u][v] , &cost[u][v]) ; // 边权 花费 
		G[v][u] = G[u][v] ;
		cost[v][u] = cost[u][v] ;
	} 
	Dijkstra(s) ; 
	DFS(t) ;
	for(int i = path.size() - 1 ; i >= 0 ; i --){
		printf("%d " , path[i]) ;
	} 
	printf("%d %d" , d[t] , minCost) ;
	return 0;
}

邻接表 + Bellman - Ford + DFS
#include<bits/stdc++.h>
using namespace std ;
const int maxv = 520 ;
const int inf = 0x3fffffff ;
struct Node{
	int v ; // 邻接边的目标顶点
	int dis ; // 邻接边的边权 
	Node(int _v , int _dis) : v(_v) , dis(_dis) {} ; // 构造方法 
};
vector<Node> Adj[maxv] ; // 图 G 的邻接表 
int d[maxv] ; // 记录源点到各点的最短路径 
set<int> pre[maxv] ; // 记录结点的前驱结点
int n , m , s , t ; // 顶点数 边数 起点 终点 
int cost[maxv][maxv] ; // 各边的花费 
vector<int> path , temp ;
int minCost = inf ; // 若需计算最短路径条数,则可增加全局变量 ans ,DFS遍历到叶子节点时 加 1 
void Bellman(int s){
	fill(d , d + maxv , inf) ;
	d[s] = 0 ;
	for(int i = 0 ; i < n - 1 ; i ++){
		for(int u = 0 ; u < n ; u ++){
			for(int j = 0 ; j < Adj[u].size() ; j ++){
				int v = Adj[u][j].v ;
				int dis = Adj[u][j].dis ;
				if(d[u] + dis < d[v]){ // 以 u 为中介点可以使 d[v] 更小 
					d[v] = dis + d[u] ; // 覆盖 d[v] 
					pre[v].clear() ; // 清空 pre[v] 
					pre[v].insert(u) ; // 令 v 的前驱为 u 
				}else if(d[u] + dis == d[v]){ // 相同的最短路径
					pre[v].insert(u) ; //令 v 的前驱为 u 
				}
			}
		}
	} 
}
void DFS(int v){
	if(v == s){ // 递归边界
		temp.push_back(v) ; // 将 起点 加入路径最后面
		int tempCost = 0 ;
		for(int i = temp.size() - 1 ; i > 0 ; i --){
			int id = temp[i] , idNext = temp[i - 1] ;
			tempCost += cost[id][idNext] ; // 增加边 id -> idNext 的边权花费 
		}
		if(tempCost < minCost){
			minCost = tempCost ; // 更新最小花费 
			path = temp ; //更新路径 
		} 
		temp.pop_back() ; // 将刚加入的结点删除
		return ;
	} 
	temp.push_back(v) ; // 将 v 加入路径
	set<int>::iterator it ;
	for(it = pre[v].begin() ; it != pre[v].end() ; it ++) DFS(*it) ;
	temp.pop_back() ; // 将刚加入的结点删除
} 
int main(){
	scanf("%d%d%d%d" , &n , &m , &s , &t) ;
	int u , v , dis , co ;
	for(int i = 0 ; i < m ; i ++){
		scanf("%d%d%d%d" , &u , &v , &dis , &co) ;
		Adj[u].push_back(Node(v , dis)) ;
		Adj[v].push_back(Node(u , dis)) ;
		cost[u][v] = cost[v][u] = co ;
	}
	Bellman(s) ;
	DFS(t) ;
	for(int i = path.size() - 1 ; i >= 0 ; i --)
		printf("%d " , path[i]) ; // 输出最优路径 
	printf("%d %d\n" , d[t] , minCost) ; 
	return 0;
}
邻接表 + SPFA + DFS
#include<bits/stdc++.h>
using namespace std ;
const int maxv = 520 ;
const int inf = 0x3fffffff ;
struct Node{
	int v ; // 邻接边的目标顶点
	int dis ; // 邻接边的边权 
	Node(int _v , int _dis) : v(_v) , dis(_dis) {} ; // 构造方法 
};
vector<Node> Adj[maxv] ; // 图 G 的邻接表 
int d[maxv] ; // 记录源点到各点的最短路径 
set<int> pre[maxv] ; // 记录结点的前驱结点
int n , m , s , t ; // 顶点数 边数 起点 终点 
int cost[maxv][maxv] ; // 各边的花费 
bool inq[maxv] ;  // 标记是否在队中 
int num[maxv] ; // 记录顶点的入队次数 
vector<int> path , temp ;
int minCost = inf ; // 若需计算最短路径条数,则可增加全局变量 ans ,DFS遍历到叶子节点时 加 1 
void SPFA(int s){
	// 初始化 
	fill(d , d + maxv , inf) ;
	memset(inq , false , sizeof(inf)) ;
	memset(num , 0 , sizeof(num)) ;
	// 源点入队
	d[s] = 0 ; // 源点到自身的距离为 0
	queue<int> q ; // 定义队列
	q.push(s) ; // 源点入队
	inq[s] = true ; // 标记源点入队
	num[s] ++ ; // 源点入队次数 + 1
	while(!q.empty()){
		int u = q.front() ; // 获取队首元素
		q.pop() ; // 队首元素出队
		inq[u] = false ; // 设置 u 不在队中
		for(int j = 0 ; j < Adj[u].size() ; j ++){
			int v = Adj[u][j].v ; // 邻接边的目标顶点
			int dis = Adj[u][j].dis ; // 邻接边的边权
			if(d[u] + dis > d[v]) continue ;
			if(!inq[v]){
				q.push(v) ; // 将 v 入队
				inq[v] = true ; // 设置 v 已入队
				num[v] ++ ; // v 的入队次数 + 1
				if(num[v] >= n) return ;//有负环 
			}
			if(d[u] + dis < d[v]){ // 以 u 为中介点可以使 d[v] 更优 
				d[v] = d[u] + dis ; // 覆盖 d[v] 
				pre[v].clear() ; // 清空 pre[v]
				pre[v].insert(u) ; // 令 u 为 v 的前驱 
//				if(!inq[v]){
//					q.push(v) ; // 将 v 入队
//					inq[v] = true ; // 设置 v 已入队
//					num[v] ++ ; // v 的入队次数 + 1
//					if(num[v] >= n) return ;//有负环 
//				}
			}else if(d[u] + dis == d[v]){// 相同的最短路径 
				pre[v].insert(u) ;
//				if(!inq[v]){
//					q.push(v) ; // 将 v 入队
//					inq[v] = true ; // 设置 v 已入队
//					num[v] ++ ; // v 的入队次数 + 1
//					if(num[v] >= n) return ;//有负环 
//				} 
			} 
		} 
	} 
}
void DFS(int v){
	if(v == s){ // 递归边界
		temp.push_back(v) ; // 将 起点 加入路径最后面
		int tempCost = 0 ;
		for(int i = temp.size() - 1 ; i > 0 ; i --){
			int id = temp[i] , idNext = temp[i - 1] ;
			tempCost += cost[id][idNext] ; // 增加边 id -> idNext 的边权花费 
		}
		if(tempCost < minCost){
			minCost = tempCost ; // 更新最小花费 
			path = temp ; //更新路径 
		} 
		temp.pop_back() ; // 将刚加入的结点删除
		return ;
	} 
	temp.push_back(v) ; // 将 v 加入路径
	set<int>::iterator it ;
	for(it = pre[v].begin() ; it != pre[v].end() ; it ++) DFS(*it) ;
	temp.pop_back() ; // 将刚加入的结点删除
} 
int main(){
	scanf("%d%d%d%d" , &n , &m , &s , &t) ;
	int u , v , dis , co ;
	for(int i = 0 ; i < m ; i ++){
		scanf("%d%d%d%d" , &u , &v , &dis , &co) ;
		Adj[u].push_back(Node(v , dis)) ;
		Adj[v].push_back(Node(u , dis)) ;
		cost[u][v] = cost[v][u] = co ;
	}
	SPFA(s) ;
	DFS(t) ;
	for(int i = path.size() - 1 ; i >= 0 ; i --)
		printf("%d " , path[i]) ; // 输出最优路径 
	printf("%d %d\n" , d[t] , minCost) ; 
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lingchen0522

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

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

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

打赏作者

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

抵扣说明:

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

余额充值