[最短路变形]Heavy Transportation POJ1797

48 篇文章 0 订阅

Background
Hugo Heavy is happy. After the breakdown of the Cargolifter project he can now expand business. But he needs a clever man who tells him whether there really is a way from the place his customer has build his giant steel crane to the place where it is needed on which all streets can carry the weight.
Fortunately he already has a plan of the city with all streets and bridges and all the allowed weights.Unfortunately he has no idea how to find the the maximum weight capacity in order to tell his customer how heavy the crane may become. But you surely know.
Problem
You are given the plan of the city, described by the streets (with weight limits) between the crossings, which are numbered from 1 to n. Your task is to find the maximum weight that can be transported from crossing 1 (Hugo's place) to crossing n (the customer's place). You may assume that there is at least one path. All streets can be travelled in both directions.

Input

The first line contains the number of scenarios (city plans). For each city the number n of street crossings (1 <= n <= 1000) and number m of streets are given on the first line. The following m lines contain triples of integers specifying start and end crossing of the street and the maximum allowed weight, which is positive and not larger than 1000000. There will be at most one street between each pair of crossings.

Output

The output for every scenario begins with a line containing "Scenario #i:", where i is the number of the scenario starting at 1. Then print a single line containing the maximum allowed weight that Hugo can transport to the customer. Terminate the output for the scenario with a blank line.

Sample Input

1
3 3
1 2 3
1 3 4
2 3 5

Sample Output

Scenario #1:
4

题意: 求1~n所有路径中最小值的最大值。

分析: dijkstra最短路的变形,定义dis[i]为从起点到i点的所有路径中最小值的最大值,若已知dis[now],遍历now的所有连边,其权值分别为edge[i].w,若dis[to] < min(dis[now], edge[i].w),则dis[to] = min(dis[now], edge[i].w),这时dis[to]的值就是过点now(表示to的上一个点是now)的所有路径上最小值的最大值。正确性分情况讨论即可,要么edge[i].w大于等于dis[now],要么小于dis[now]。

之后考虑优先队列的顺序,由于dijkstra算法先加入点集的点dis值一定是正确的,之后一定不会被更新,因此应该先加入dis值大的点(画图思考一下),所以应采用降序的优先队列。

这道题到这里就结束了,但是为什么套用dijkstra算法是正确的呢,明明dis数组的含义都改变了。其实只要搞清楚为什么当前dis值最大的点被加入集合后dis值不会再被其他点更新。下面模拟一下这个过程,样例如图:

首先把起点1加入集合,这个没什么可说的,用蓝圈表示该点被加入集合,之后更新起点相邻的点dis值,松弛规则还是按照本题要求,若dis[to] < min(dis[now], edge[i].w),则dis[to] = min(dis[now], edge[i].w),dis值用蓝字标注在点的下方。

接下来从优先队列中取出dis最大的点加入集合,并更新其相邻点dis值,如下图:

 和上一步一样,取出dis最大的点加入集合,并更新其相邻点dis值,如下图:

接下来应该把点6加入集合,这表示从起点到点6的任何路径上的最小边的最大值为dis[6],但是dis[6]只被点2更新过一次,这表示该值是经过点2的从起点到点6的任何路径上的最小边的最大值,会不会经过点4或者点5到达点6可以得到一个更大的dis值呢?这其实是不行的,请看下图:

黄色圈是已经加入集合的点形成的,可以把它看成一个连通块。考虑连通块四周相邻的点,这些点dis值都被连通块边界处的点更新过,也就是说它们的dis值是从起点到该点且经过连通块边界点的所有路径最小边的最大值。因此任意一条从起点到连通块四周相邻点k的路径的最小边一定小于等于dis[k],又由于从起点到连通块外任何一点t都需要先出连通块,假设块外第一个点为k,那么可以把路径分成两部分,一部分是从起点到k,一部分是从k到t,前者路径上最小边的最大值为dis[k],所以任何一条前者路径最小边都小于等于dis[k],这样整条路经的最小边也一定小于等于dis[k]。再回到上图情况中,dis[6]显然大于等于任何dis[k](k任取连通块四周相邻的点),这说明从起点到连通块外任何一点的所有路径上的最小边都小于等于dis[6],或者是从起点到连通块外任何一点的所有路径上必定有一条边值小于等于dis[6],这就说明了点4和点5一定无法更新点6的dis值,因为到它们的所有路径都会经过一条小于等于dis[6]的边,这就决定了这些路径上的最小边也一定小于等于dis[6],无法让dis[6]变大。

具体代码如下: 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define inf 0x3f3f3f3f
#define pii pair<int, int>
using namespace std;
//求1~n所有路径上最小值的最大值 
int n, m, head[1005], vis[1005], dis[1005], cnt;//dis[i]表示从起点到i点的所有路径中最小值的最大值 
struct edge
{
	int to, next, w;
}edge[2000005];

void add(int u, int v, int w)
{
	edge[++cnt].to = v;
	edge[cnt].w = w;
	edge[cnt].next = head[u];
	head[u] = cnt;
}

void dijkstra()
{
	priority_queue<pii> a;
	dis[1] = inf;
	a.push(make_pair(inf, 1));
	while(a.size())
	{
		int now = a.top().second;
		a.pop();
		if(vis[now])
			continue;
		vis[now] = 1;
		for(int i = head[now]; i; i = edge[i].next)
		{
			int to = edge[i].to;
			if(min(dis[now], edge[i].w) > dis[to])
			{
				dis[to] = min(dis[now], edge[i].w);
				a.push(make_pair(dis[to], to));
			}
		}
	}
}

signed main()
{
	int T;
	cin >> T;
	for(int i = 1; i <= T; i++)
	{
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i++)
			head[i] = vis[i] = dis[i] = 0;
		cnt = 0;
		for(int i = 1; i <= m; i++)
		{
			int u, v, w;
			scanf("%d%d%d", &u, &v, &w);
			add(u, v, w), add(v, u, w);
		}
		dijkstra();
		printf("Scenario #%d:\n%d\n\n", i, dis[n]);
	}
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值