PAT (Advanced Level) Practice 1003 Emergency (25 分) 凌宸1642

PAT (Advanced Level) Practice 1003 Emergency (25 分) 凌宸1642

题目描述:

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

译:作为一个城市的紧急救援队的队长,你会得到一张你国家的特殊地图。这张地图显示了几个分散的城市,它们之间有一些道路相连。每个城市的救援队伍数量和每对城市之间的道路长度都标在地图上。当其他城市向你发出紧急呼叫时,你的任务是带领你的人尽快赶到那个地方,同时,在路上召集尽可能多的人。


Input Specification (输入说明):

Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (≤500) - the number of cities (and the cities are numbered from 0 to N−1), M - the number of roads, C1 and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1. to C2.

译:每个输入文件包含一个测试用例。对于每个测试用例,第一行包含4个正整数:N(≤500)-城市的数量(城市的编号从0到N−1),M -道路的数量,C1和C2 -你目前所在的城市和你必须保存的城市,分别。下一行包含N个整数,第i个整数是第i个城市的救援队数量。然后是M条线,每条线都描述了一条有三个整数c1, c2和L的道路,分别是由道路连接的一对城市和道路的长度。它保证至少存在一条从C1到C2的路径。


Output Specification (输出说明):

For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line…

译:对于每个测试用例,在一行中打印两个数字:C1 和 C2之间不同最短路径的数量,以及您可能聚集的最大救援队伍数量。一行中的所有数字必须用一个空格隔开,行尾不允许有额外的空格。。


Sample Input (样例输入):

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

Sample Output (样例输出):

2 4

The Idea:

最短路径的获得 最大点权 问题。

The Codes:

// 邻接矩阵 + Dijkstra
#include<bits/stdc++.h>
using namespace std ;
const int maxv = 520 ;
const int inf = 100000000 ;
int G[maxv][maxv] ; // n 为顶点数 , G 图的邻接矩阵 
bool vis[maxv] = { false } ;// 标记数组
int num[maxv] ; // 最短路径条数
int weight[maxv] ; // 点权
int d[maxv] ; // 起点到各点的最短路径长度
int w[maxv] ; //记录最大点权之和 
int n , m , s , t ; // 顶点数 边数 起点 终点 

void Dijkstra(int s){
	fill(d , d + maxv , inf) ; // fill 函数将整个 d 数组 赋值为 INF
	memset(num , 0 , sizeof(num)) ;
	memset(w , 0 , sizeof(w)) ;
	d[s] = 0 ; // 起点到自身的距离为 0
	num[s] = 1 ; // 起点到自身的最短路径条数为 1
	w[s] = weight[s] ; // 初始化 w[s] 为 s 的点权 
	for(int i = 0 ; i < n ; i ++){ // 循环 n 次 
		int u = -1 , minv = inf ;
		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){ // 以 u 为中介点 能到达 v 
				if(d[u] + G[u][v] < d[v]){  
					d[v] = d[u] + G[u][v] ; //优化 d[v]  
					num[v] = num[u] ; // num[u] 赋值给 num[v] 
					w[v] = w[u] + weight[v] ; // s 到 v 的点权和 为 到 u 的点权和加 weight[v] 
				}else if(d[u] + G[u][v] == d[v]){ // 有相同的最短距离 
					if(w[u] + weight[v] > w[v]){ // 如果可以得到更大的点权和  
						w[v] = w[u] + weight[v] ;  // 更新 v 的点权和 
					}
					num[v] += num[u] ; //num[u] 累加到 num[v] 上 
				}
			}
		} 
	} 
}
int main(){
	scanf("%d%d%d%d" , &n , &m , &s , &t) ; // 输入 顶点数 边数 起点 终点 
	for(int i =0 ; i < n ; i ++){
		scanf("%d" , &weight[i]) ; // 各点的 点权 
	} 
	int u , v ; 
	fill(G[0] , G[0] + maxv * maxv , inf) ; // 初始化图 
	for(int i =0 ; i < m ; i ++){
		scanf("%d%d" , &u , &v) ; // 边 u -> v  
		scanf("%d" , &G[u][v]) ; // 输入 边权 
		G[v][u] = G[u][v] ; // 无向边变两条有方向的有向边 
	} 
	Dijkstra(s) ; 
	printf("%d %d\n" , num[t] , w[t]) ; // 输出最短路径条数 和 最大点权和 
	return 0;
}

// 邻接表 + Dijkstra 
#include<bits/stdc++.h>
using namespace std ;
const int maxv = 520 ;
const int inf = 100000000 ;
struct Node{
	int v ; // 边的终点
	int dis ; // 边权 
	Node(int _v , int _dis) : v(_v) , dis(_dis) {} ; // 构造方法 
};
vector<Node> Adj[maxv] ;/// G 图的邻接表 
bool vis[maxv] = { false } ;// 标记数组
int num[maxv] ; // 最短路径条数
int weight[maxv] ; // 点权
int d[maxv] ; // 起点到各点的最短路径长度
int w[maxv] ; //记录最大点权之和 
int n , m , s , t ; // 顶点数 边数 起点 终点 

void Dijkstra(int s){
	fill(d , d + maxv , inf) ; // fill 函数将整个 d 数组 赋值为 INF
	memset(num , 0 , sizeof(num)) ;
	memset(w , 0 , sizeof(w)) ;
	d[s] = 0 ; // 起点到自身的距离为 0
	num[s] = 1 ; // 起点到自身的最短路径条数为 1
	w[s] = weight[s] ; // 初始化 w[s] 为 s 的点权 
	for(int i = 0 ; i < n ; i ++){ // 循环 n 次 
		int u = -1 , minv = inf ;
		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 j = 0 ; j < Adj[u].size() ; j ++){
			int v = Adj[u][j].v ;
			if(vis[v] == false){
				if(d[u] + Adj[u][j].dis < d[v]){
					d[v] = d[u] + Adj[u][j].dis ; //优化 d[v]  
					num[v] = num[u] ; // num[u] 赋值给 num[v] 
					w[v] = w[u] + weight[v] ; // s 到 v 的点权和 为 到 u 的点权和加 weight[v] 
				}else if(d[u] + Adj[u][j].dis == d[v]){
					if(w[u] + weight[v] > w[v]){ // 如果可以得到更大的点权和  
						w[v] = w[u] + weight[v] ;  // 更新 v 的点权和 
					}
					num[v] += num[u] ; //num[u] 累加到 num[v] 上 
				}
			} 
		}
	} 
}
int main(){
	scanf("%d%d%d%d" , &n , &m , &s , &t) ; // 输入 顶点数 边数 起点 终点 
	for(int i =0 ; i < n ; i ++){
		scanf("%d" , &weight[i]) ; // 各点的 点权 
	} 
	int u , v , di ; 
	for(int i =0 ; i < m ; i ++){
		scanf("%d%d%d" , &u , &v , &di) ; // 边 u -> v  边权 di 
		Adj[u].push_back(Node(v , di)) ; // 无向边变两条有方向的有向边
		Adj[v].push_back(Node(u , di)) ;
	} 
	Dijkstra(s) ; 
	printf("%d %d\n" , num[t] , w[t]) ; // 输出最短路径条数 和 最大点权和 
	return 0;
}

// 邻接矩阵 + Dijkstra + DFS
#include<bits/stdc++.h>
using namespace std ;
const int maxv = 520 ;
const int inf = 100000000 ;
int G[maxv][maxv] ; // n 为顶点数 , G 图的邻接矩阵 
bool vis[maxv] = { false } ;// 标记数组
int weight[maxv] ; // 点权
int d[maxv] ; // 起点到各点的最短路径长度
int w[maxv] ; //记录最大点权之和 
int n , m , s , t ; // 顶点数 边数 起点 终点 
vector<int> pre[maxv] ; // 记录结点的前驱结点 
vector<int> path , temp ; // 最优路径 临时路径
int ans = 0 , maxNum = 0 ; // 最短路径条数 ,最多人数 
void Dijkstra(int s){
	fill(d , d + maxv , inf) ; // fill 函数将整个 d 数组 赋值为 INF
	d[s] = 0 ; // 起点到自身的距离为 0
	for(int i = 0 ; i < n ; i ++){ // 循环 n 次 
		int u = -1 , minv = inf ;
		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){ // 以 u 为中介点 能到达 v 
				if(d[u] + G[u][v] < 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){
		ans ++ ; // 到达叶子结点 , 最短路径条数加 1
		temp.push_back(v) ; // 将起点加入临时路径的最后
		int tempNum = 0 ;
		for(int i = temp.size() - 1 ; i >= 0 ; i -- ){
			tempNum += weight[temp[i]] ;
		}
		if(tempNum > maxNum){ // 当前点权大于最优值
			maxNum = tempNum ;  // 更新最优值
			path = temp ; // 更新最优路径
		}
		temp.pop_back() ; 
		return ;
	} 
	temp.push_back(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) ; // 输入 顶点数 边数 起点 终点 
	for(int i =0 ; i < n ; i ++){
		scanf("%d" , &weight[i]) ; // 各点的点权 
	} 
	int u , v ; 
	fill(G[0] , G[0] + maxv * maxv , inf) ; // 初始化图 
	for(int i =0 ; i < m ; i ++){
		scanf("%d%d" , &u , &v) ; // 边 u -> v  
		scanf("%d" , &G[u][v]) ; // 输入 边权 
		G[v][u] = G[u][v] ; // 无向边变两条有方向的有向边 
	} 
	Dijkstra(s) ; 
	DFS(t) ;
//	for(int i = path.size() - 1 ; i >= 0 ; i --){
//		printf("%d " , path[i]) ;
//	} 
	printf("%d %d\n" , ans , maxNum) ; // 输出最短路径条数 和 最大点权和 
	return 0;
}

// 邻接表 + Dijkstra + DFS
#include<bits/stdc++.h>
using namespace std ;
const int maxv = 520 ;
const int inf = 100000000 ;
struct Node{
	int v ; // 边的终点
	int dis ; // 边权 
	Node(int _v , int _dis) : v(_v) , dis(_dis) {} ; // 构造方法 
};
vector<Node> Adj[maxv] ;/// G 图的邻接表 
bool vis[maxv] = { false } ;// 标记数组
int weight[maxv] ; // 点权
int d[maxv] ; // 起点到各点的最短路径长度
int n , m , s , t ; // 顶点数 边数 起点 终点 
vector<int> pre[maxv] ;
vector<int> path , temp ;
int ans = 0 , maxNum = 0 ;
void Dijkstra(int s){
	fill(d , d + maxv , inf) ; // fill 函数将整个 d 数组 赋值为 INF
	d[s] = 0 ; // 起点到自身的距离为 0
	for(int i = 0 ; i < n ; i ++){ // 循环 n 次 
		int u = -1 , minv = inf ;
		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 j = 0 ; j < Adj[u].size() ; j ++){
			int v = Adj[u][j].v ;
			if(vis[v] == false){
				if(d[u] + Adj[u][j].dis < d[v]){
					d[v] = d[u] + Adj[u][j].dis ; //优化 d[v]  
					pre[v].clear() ;
					pre[v].push_back(u) ;
				}else if(d[u] + Adj[u][j].dis == d[v]){
					pre[v].push_back(u) ;
				}
			} 
		}
	} 
}
void DFS(int v){
	if(v == s){  // 递归边界
		ans ++ ; // 到达叶子结点 最短路径条数 加 1 
		temp.push_back(v) ; // 将起点加入临时路径最后面 
		int tempNum = 0 ; // 记录此次路径的 最大人数 
		for(int i = temp.size() - 1 ; i >= 0 ; i --){
			tempNum += weight[temp[i]] ; // 累加点权 
		}
		if(tempNum > maxNum){ // 如果最大人数大于当前最大人数 
			maxNum = tempNum ; // 更新最优值 
			path = temp ; // 更新最优路径 
		}
		temp.pop_back() ;
		return ;
	} 
	temp.push_back(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) ; // 输入 顶点数 边数 起点 终点 
	for(int i =0 ; i < n ; i ++){
		scanf("%d" , &weight[i]) ; // 各点的 点权 
	} 
	int u , v , di ; 
	for(int i =0 ; i < m ; i ++){
		scanf("%d%d%d" , &u , &v , &di) ; // 边 u -> v  边权 di 
		Adj[u].push_back(Node(v , di)) ; // 无向边变两条有方向的有向边
		Adj[v].push_back(Node(u , di)) ;
	} 
	Dijkstra(s) ;
	DFS(t) ; 
	printf("%d %d\n" , ans , maxNum) ; // 输出最短路径条数 和 最大点权和 
	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、付费专栏及课程。

余额充值