HDU3631 Shortest Path——Floyd巧妙应用(⭐)

点这里

题意: 给定一个加权有向图,有n个点、m条边以及q条操作。q条操作中,有两种类型0 u表示将u点做上标记,1 u v表示计算u点和v的最短距离, 要求是最短路径上包括u、v的所有点都做上标记。 针对不同情况:

  • ERROR! At point x: 重复给x做标记则输出该语句。
  • ERROR! At path x to y: 要求最短路的两端点并未全做上标记则输出该语句。
  • No such path: 两端点都已做上标记,但是这两点间没有路。
  • 最短距离: 两点都已做上标记,并且两点间存在最短距离。

题解: n ≤ 300,所以用Floyd也许能行 。但是Q ≤ 100000绝对不是令人好受的范围。我也是尝试了很久才找到的最佳办法。在这里插入图片描述

  • 多次Floyd: 这是毋庸置疑的,因为要求的最短路上的所有点都必须是做过标记的才行,所以每次有新的标记出现,我们都可能可以更新出新的最短路。但是原生的Floyd模板是不行的,三重循环的时间复杂度太高了。
  • Floyd变形: 因此我们要尽量想办法让我们的Floyd适用这个题目,并且时间复杂度能够接受。办法是缩到二重循环。 可以回忆一下Floyd的核心思想是什么,是希望能在两端点u、v之间,找到一个中间节点k,使得u->k->v的距离小于u->v的直接距离。那么我们可以在每次给出一个新的标记k时,去更新所有经过k点的最短路。、
  • 标记: 然后你还可能会犯迷糊,虽然但是,按照上面的思路,我在更新的时候,最短路的两端没做过标记怎么办,需要避开不更新吗。答案是每当出现一个新的标记k,就去更新所有可能的最短路。 而为了应对更新的最短路中两端点是为标记的情况,我们只要在输出时另加判断即可。

过程中犯的错:

  • 三重Floyd: 办法还没考虑成熟就着急写代码了,所以才出现了上图的那么多错误。掌握Floyd的核心思想的话,改成二重循环即可完成题目。
  • Floyd的位置: 有想过将Floyd放在什么位置(出现新标记时、询问两点最短路时)可能会省一些时间复杂度。实际上按照我们上面的思路,每出现一个新的标记,我们就要跑一边Floyd去更新我们的最短路。
  • 标记: 在我最初的Floyd函数中,我更新的最短路,所有的点都是做过标记的。这样是不行的,这样的更新是不会完整的。前面我说可能出现更新的最短路中两端点并未标记的情况,只要在输出之前判断两点如果做过标记,我就输出具体数值;如果并未做上标记,我就输出ERROR! At path x to y

#include<bits/stdc++.h>
using namespace std;
const int N = 310;
const int inf = 0x3f3f3f3f;

int n, m, q, k, a, b, c;
int vis[N];
int dis[N][N];
void floyd(int k){
	for(int i = 0; i < n; i++)
		for(int j = 0; j < n; j++)
			dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
int main(){
	while(~scanf("%d%d%d", &n, &m, &q) && (n + m + q)){
		if(k)	puts("");
		for(int i = 0; i < n; i++){
			for(int j = 0; j < n; j++)	dis[i][j] = inf;
			vis[i] = dis[i][i] = 0;
		}
		while(m--){
			scanf("%d%d%d", &a, &b, &c);
			dis[a][b] = min(dis[a][b], c);
		}
		printf("Case %d:\n", ++k);
		while(q--){
			scanf("%d%d", &a, &b);
			if(!a){
				if(vis[b]){printf("ERROR! At point %d\n", b);continue;}	
				vis[b] = 1;
				floyd(b);
			}
			else{
				scanf("%d", &c);
				if(!vis[b] || !vis[c])	printf("ERROR! At path %d to %d\n", b, c);
				else{
					if(dis[b][c] == inf)	printf("No such path\n");
					else	printf("%d\n", dis[b][c]);
				}
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值