算法笔记---【PAT A1003 】Emergency

71 篇文章 0 订阅
41 篇文章 3 订阅

题目链接:【PAT A1003 】Emergency

题目描述

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, C1and 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, c​2 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 C​2.

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.

题目大意:

给出N个城市,M条无向边。每个城市中都有一定数目的救援小组,所有边的边权已知。现在给出起点和终点,求从起点到终点的最短路径条数及最短路径上的救援小组数目之和。
如果有多条最短路径,则输出数目之和最大的。

样例:

输入:
5 6 0 2// 表示5个结点,6条边,起始点为0,目的点为2
1 2 1 5 3//表示每个结点的权
0 1 1//表示 0 1 之前的边权为 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
输出:
2 4//表示起点到终点两条最短路径,点权的最大值为4

运行结果:

在这里插入图片描述
解题思路:
可以直接使用Dijkstra算法求解,但是很容易出错。
书中推荐使用Dijkstra算法+ DFS 来求解
具体过程:
1、使用Dijkstra算法求出起始结点到每个节点的最短路径和最短路径上的结点的父节点。
2、使用dfs算法遍历最短路径生成的树。
这样做的好处是,将边权值和点权值的判断分开处理。
而且给出最短路径上结点的父节点数组,就可以很方便的求解其他限制条件下的路径。

书中给出的算法是使用邻接矩阵来实现Dijkstra算法,博主使用的是邻接表来实现,且使用优先队列来获取当前结点的邻接边的最小值。
如果Dijkstra算法使用优先队列不是很清楚如何编写代码,可以看博主算法笔记—Dijkstra算法的实现。

下面为AC代码:

#include<iostream>
#include<queue>
#include<vector>
#include<map>
#include<algorithm>
#include<limits.h>
using namespace std;

const int dij_max_v = 1010;

int node_weight[dij_max_v] = { 0 };//表示每个城市的点权
int num[dij_max_v] = {0};//表示最短路径的条数
vector<int> path, temp_path;//表示最短路径
int optionVal = 0;
vector<int> parent[dij_max_v];//表示当前结点的父结点

struct DijNode
{
	int id;//当前结点id
	int weight;//到该结点的权值,即边权
};

struct MinWeightCompare//小顶堆
{
	bool operator() (const DijNode node1, const DijNode node2) const{
		return node1.weight > node2.weight;
	}
};


//************************************
// Method:    dijkstra 返回单源最短路径
// FullName:  dijkstra
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: vector<DijNode> matrix[] 给定的图
// Parameter: DijNode start 初始结点
// Parameter: bool * visited 表示当前结点是否被访问
// Parameter: vector<int> parent[] 表示当前到结点的上一个结点
// Parameter: map<int,int> & distance 表示初始结点到当前结点的最短路径
//************************************
void dijkstra(vector<DijNode> matrix[], DijNode start, bool* visited, vector<int> parent[], map<int, int>& distance) {
	
	priority_queue<DijNode, vector<DijNode>, MinWeightCompare> p_queue;//BFS 队列
	//该优先队列中的元素表示到当前结点的最短路径。
	num[start.id] = 1;//表示当前结点的最短路径只有1条
	p_queue.push(start);

	while (!p_queue.empty())
	{
		DijNode top_node = p_queue.top();
		p_queue.pop();
		visited[top_node.id] = true;//标记当前结点已被访问
		for (int i = 0;i < matrix[top_node.id].size();i++)
		{
			DijNode next_node = matrix[top_node.id][i];
			//当前结点没有被访问,且以 top_node 为中介点可以使得 next_node 更优
			if (visited[next_node.id] == false)
			{
				if (distance[top_node.id] + next_node.weight < distance[next_node.id])
				{
					distance[next_node.id] = distance[top_node.id] + next_node.weight;
					DijNode node;
					node.id = next_node.id;
					node.weight = distance[next_node.id];//更新当前的最短距离
					num[next_node.id] = num[top_node.id];//更新当前路径的条数
					p_queue.push(node);//将所遍历的结点加入到队列中
					parent[next_node.id].clear();//清空parent 当前结点数组
					parent[next_node.id].push_back(top_node.id);//更新所遍历结点的父结点
				}else if (distance[top_node.id] + next_node.weight == distance[next_node.id])//表示最短距离相等
				{
					parent[next_node.id].push_back(top_node.id);//更新所遍历结点的父结点
					num[next_node.id] += num[top_node.id];//更新最短路径的条数
				}
				
			}
		}
	}
}

//打印最短路径所经过的结点
void print_path(vector<int>& path) {
	for (int i = path.size() - 1;i >= 0;i--)
	{
		cout << path[i] << " ";
	}
}

//求出最优解
void dfs(int start_node, int end_node) {

	if (end_node == start_node)
	{
		temp_path.push_back(end_node);
		int value = 0;
		//计算点权
		for (vector<int>::iterator it = temp_path.begin(); it != temp_path.end(); it++)
		{
			value += node_weight[(*it)];
		}
		if (value > optionVal)
		{
			optionVal = value;
			path = temp_path;
		}
		temp_path.pop_back();
		return;
	}
	temp_path.push_back(end_node);
	for (int i = 0;i < parent[end_node].size();i++)
	{
		dfs(start_node, parent[end_node][i]);
	}
	temp_path.pop_back();
}


int main() {

	int n, m, st, ed;//表示顶点个数,边数,初始结点,结束结点
	cin >> n >> m >> st >> ed;
	for (int i = 0;i < n;i++)
	{
		cin >> node_weight[i];
	}

	int form_node, to_node, weight;//表示起始结点,终止结点,权值
	vector<DijNode> matrix[dij_max_v];//用来存放图
	DijNode node;

	map<int, int> distance;//记录起始结点到其他结点的最短路径
	distance[st] = 0;//自己到自己的距离为 0 
	for (int i = 0;i < m;i++)
	{
		cin >> form_node >> to_node >> weight;
		//注意是无向图
		node.id = to_node;
		node.weight = weight;
		matrix[form_node].push_back(node);

		node.id = form_node;
		node.weight = weight;
		matrix[to_node].push_back(node);

		if (to_node != st)
		{
			distance[to_node] = INT_MAX;//表示除了起始结点,剩下的皆为不可达
		}
	}
	
	bool visited[dij_max_v];
	fill(visited, visited + dij_max_v, false);
	node.id = st;//起始结点的id
	node.weight = 0;//起始结点距离自己的距离
	
	parent[st].push_back(-1);//初始化起始结点的父结点
	dijkstra(matrix, node, visited, parent, distance);
	dfs(st, ed);
	cout << num[ed] << " " <<optionVal;
	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值