PAT1003. Emergency (25)

题目如下:

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

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.

Output

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.

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
题目的意思其实就是给了几个点之间的距离,几个点上的队伍数,给定其中2个点,求这两个点的最短路径数量,以及走一条路径经过的点上的队伍数最多时的队伍数。那么看到这个题目我就想到用迪杰斯特拉算法(最短路径)来做,顺便也复习了一下迪杰斯特拉算法。

迪杰斯特拉算法主要就是用来在给定带权图时求从一个点到其余顶点的最短路径。是按路径长度递增的次序产生最短路径的算法。假设v为起点,S为已产生最短路径的终点的集合,先将v加入该集合,用D[i]记录v初始状态到每个顶点的距离。一开始在D中找到一条最短路径,将新找到最短路径的顶点vk加入S集,然后更新D,看通过vk到达其余点的距离是否比之前的距离要短,如果短则更新。在更新后再在D中找一条最短路径,以此类推,就可以找到所有最短路径。

放到这个题目中,需要用一个数组amount记录队伍数,初始我是将所有的amount设为C1的队伍数加上该点的队伍数。用count记录最短路径条数,初始就连通的设为1,不连通的设为0。然后在更新距离时,如果存在比之前距离短的路径,则将其的amount设为 之前找到最短路径vk的 amount加上当前点的队伍数,将count值 设为和之前找到的vk的count值一样即可。如果存在和之前距离一样的路径,则对比amount,选择比较多的值为新的amount,count值要在本身的基础上加上通过vk这条路的count即可。代码如下:

#include <iostream>
#define INFINITE 65536
using namespace std;

int main()
{
	int N, M, C1, C2;
	cin >> N >> M >> C1 >> C2;
	int c1, c2, L; 
	int *teams = new int[N];
	for (int i = 0; i < N;i++)
		cin >> teams[i];

	//初始化邻接矩阵
	int **length = new int*[N];
	for (int i = 0;i < N;i++)
		length[i] = new int[N];
	for (int i = 0;i < N;i++)
	{
		for (int j = 0;j < N;j++)
		{
			length[i][j] = INFINITE ;				//邻接矩阵先全部设为最大
		}
	}
	for (int i = 0;i < M;i++)
	{
		cin >> c1 >> c2 >> L;                    //读入两个城市之间的距离,修改邻接矩阵
		length[c1][c2] = L;
		length[c2][c1] = L;
	}
	//迪杰斯特拉算法
	bool* finished = new bool[N];  //c1到每个定点是否已找到最短路径
	int* distance = new int[N];		//c1到每个定点的距离
	int* count = new int[N];   //记录从c1到每个点的最短路径条数
	int* amount = new int[N];	// 记录c1到每个点需要的最多队伍数

	for (int i = 0;i < N;i++)    
	{
		finished[i] = false;
		distance[i] = length[C1][i];
	}	
	finished[C1] = true; distance[C1] = 0;	//将顶点C1加入S集
	for (int i = 0;i < N;i++)
	{
		amount[i] = teams[C1]+ teams[i];       //要能到一定会连通,所以初始的队伍数为在该处和C1处的队伍数
		if (distance[i] != INFINITE)
			count[i] = 1;   //存在C1直接到i的路径,初始路径为1
		else
			count[i] = 0;                //初始不存在C1直接到i的路径
	}

	for (int i = 1; i < N;i++)
	{
		int min = INFINITE;
		int v;
		for (int j = 0;j < N;j++)
		{
			if (!finished[j])
			{
				if (distance[j] < min)
				{
					min = distance[j];
					v = j;
				}
			}
		}
		finished[v] = true;   //找出了到v的最短路径后将finished[v]设为true

		for (int j = 0;j < N;j++)   //根据更新后的S集 修改distance数组
		{
			if (!finished[j])
			{
				if (min + length[v][j] < distance[j])
				{
					distance[j] = min + length[v][j];
					amount[j] = amount[v] + teams[j];  //即到j去的最短路径上一定经过v,所以去j的队伍数为去v的队伍数加上在j上的人数
					count[j] = count[v];	//去j的路径数即为去v的路径数
				}
				else if (min + length[v][j] == distance[j])
				{
					if (amount[v] + teams[j] > amount[j])   //记录最多的队伍数
						amount[j] = amount[v] + teams[j];

					count[j] += count[v];   //去j的路径数加上从v到j的路径数
					
				}	
			}
		}
	}

	cout << count[C2] << " " << amount[C2];

	return 0;
}
但是这份代码在测试时发现一个用例没有过:

后来发现是如果终点起点是一个时,初始计算amount的时候会将人数多加一次,所以修改代码如下:

#include <iostream>
#define INFINITE 65536
using namespace std;

int main()
{
	int N, M, C1, C2;
	cin >> N >> M >> C1 >> C2;
	int c1, c2, L; 
	int *teams = new int[N];
	for (int i = 0; i < N;i++)
		cin >> teams[i];

	//初始化邻接矩阵
	int **length = new int*[N];
	for (int i = 0;i < N;i++)
		length[i] = new int[N];
	for (int i = 0;i < N;i++)
	{
		for (int j = 0;j < N;j++)
		{
			length[i][j] = INFINITE ;				//邻接矩阵先全部设为最大
		}
	}
	for (int i = 0;i < M;i++)
	{
		cin >> c1 >> c2 >> L;                    //读入两个城市之间的距离,修改邻接矩阵
		length[c1][c2] = L;
		length[c2][c1] = L;
	}
	//迪杰斯特拉算法
	bool* finished = new bool[N];  //c1到每个定点是否已找到最短路径
	int* distance = new int[N];		//c1到每个定点的距离
	int* count = new int[N];   //记录从c1到每个点的最短路径条数
	int* amount = new int[N];	// 记录c1到每个点需要的最多队伍数

	for (int i = 0;i < N;i++)    
	{
		finished[i] = false;
		distance[i] = length[C1][i];
	}	
	finished[C1] = true; distance[C1] = 0;	//将顶点C1加入S集
	for (int i = 0;i < N;i++)
	{
		amount[i] = teams[C1]+ teams[i];       //要能到一定会连通,所以初始的队伍数为在该处和C1处的队伍数
		if (distance[i] != INFINITE)
			count[i] = 1;   //存在C1直接到i的路径,初始路径为1
		else
			count[i] = 0;                //初始不存在C1直接到i的路径
	}
	amount[C1] = teams[C1]; //如果是C1到C1,按之前的会算两次,所以要重新设一次

	for (int i = 1; i < N;i++)
	{
		int min = INFINITE;
		int v;
		for (int j = 0;j < N;j++)
		{
			if (!finished[j])
			{
				if (distance[j] < min)
				{
					min = distance[j];
					v = j;
				}
			}
		}
		finished[v] = true;   //找出了到v的最短路径后将finished[v]设为true

		for (int j = 0;j < N;j++)   //根据更新后的S集 修改distance数组
		{
			if (!finished[j])
			{
				if (min + length[v][j] < distance[j])
				{
					distance[j] = min + length[v][j];
					amount[j] = amount[v] + teams[j];  //即到j去的最短路径上一定经过v,所以去j的队伍数为去v的队伍数加上在j上的人数
					count[j] = count[v];	//去j的路径数即为去v的路径数
				}
				else if (min + length[v][j] == distance[j])
				{
					if (amount[v] + teams[j] > amount[j])   //记录最多的队伍数
						amount[j] = amount[v] + teams[j];

					count[j] += count[v];   //去j的路径数加上从v到j的路径数
					
				}	
			}
		}
	}

	cout << count[C2] << " " << amount[C2];

	return 0;
}

这样就能通过了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值