洛谷1119 灾后重建 很棒的floyed

一看图上的点不多,嗯,一定是floyed。然后采用询问一次就走一遍floyed函数的做法,交上去tle。。。

后来看了大佬的代码,真的对floyed理解的更加的深刻了,将的真的很棒,后来自己遇到了问题,大佬也帮忙解答了。

https://www.luogu.org/blog/Time-Rune/solution-p1119
这个算法的主要思路,就是通过其他的点进行中转来求的两点之间的最短路。因为我们知道,两点之间有多条路,如果换一条路可以缩短距离的话,就更新最短距离。而它最本质的思想,就是用其他的点进行中转,从而达到求出最短路的目的。

#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=205;
int N,M,Q;
int t[maxn];
int dp[maxn][maxn];

void init(){
	cin>>N>>M;
	for(int i=0;i<N;i++)
		scanf("%d",&t[i]);
	memset(dp,inf,sizeof(dp));
	for(int i=0;i<N;i++)
		dp[i][i]=0;
	int a,b,c;
	for(int i=0;i<M;i++){
		scanf("%d%d%d",&a,&b,&c);
		dp[a][b]=dp[b][a]=c;
	}
}

int main()
{
	init();
	cin>>Q;
	int cnt=0,i,j,a,b,tt;
	while(Q--){
		scanf("%d%d%d",&a,&b,&tt);
		for(;t[cnt]<=tt&&cnt<N;cnt++)
			for(int i=0;i<N;i++)
				for(int j=0;j<N;j++){
					dp[i][j]=min(dp[i][j],dp[i][cnt]+dp[cnt][j]);
				}
		if(inf==dp[a][b]||t[a]>tt||t[b]>tt) printf("-1\n");
		else printf("%d\n",dp[a][b]); 
	}
	return 0;
}

Q:

求助!不是很理解f[i][j]=min(f[i][j],f[i][k]+f[k][j])之前为什么不加一个判断 if(t[i]<=w&&t[j]<=w)这样不是对于i或j如果还没有建成,就不进行操作吗?不清楚这样想错在哪里。

By:ACCCCC

A:

是这样的,每个点都用成为中转点k的机会,对于不相邻的两个点,我们是依次找出用哪几个点来中转可以使得路程最短,所以每次枚举k的过程就是一个寻找中转点的过程

如果要用两个点来中转,肯定是先有一个中转点k1,然后目前枚举的是中转点k2,如果k2到k1的距离加上k2到另一个点的距离比只用k1小,那么我们就知道使用k1,k2一定是更优的。

仔细看这个过程,k2到k1的距离是在用k1中转时已经知道的,否则无法知道是否用这两个点比只用一个点更优。假设k2的修建时间晚于k1,那么如果在用k1更新各个点时跳过了k2,那么用k2来确认k1时,就无法知道k1,k2的距离,从而过程出现差错,无法继续更新(大佬的这个例子给的很好,因为cnt是一直在递增的,对于k1,k2他们都只有一次循环更新的机会,k1更新时,因为我们的封路,导致k1,k2之间的距离不能够更新,最终导致程序出错)。

再说详细一点,如果在求中转点时考虑了左右端点,那么就是说因为这个端点目前不可用而把这个端点连接的路也给封了,这样就与实现过程相矛盾,因为路是不会因为端点被封闭的

扫描二维码关注公众号,回复: 4624447 查看本文章

或者说,把端点是否可以使用与路线是否可用分开处理,我们处理各个转移点时是说这个节点可以用来转移了,然后再计算是否可以通过这个节点获得一条新的更短的路。然后输出时再判断两端的节点是否可用,如果“两端的节点均可用”且“中间所有的转移节点均可用”才能确定这条路能走,这是两个独立的过程,不能混在一起处理

// 怎么说呢....感觉还是解释的不是很清楚,因为这个Floyd本身是可以类似图上DP的过程,挨个分析一步步的过程感觉很难说清楚,所以我想了四十分钟左右也只能尽可能的给出一个合理一点的答案了,希望能理解吧(笑~)

猜你喜欢

转载自blog.csdn.net/qq_41755258/article/details/84898093
今日推荐