1. 程式人生 > >洛谷1119 災後重建 很棒的floyed

洛谷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之間的距離不能夠更新,最終導致程式出錯)。

再說詳細一點,如果在求中轉點時考慮了左右端點,那麼就是說因為這個端點目前不可用而把這個端點連線的路也給封了,這樣就與實現過程相矛盾,因為路是不會因為端點被封閉的

或者說,把端點是否可以使用與路線是否可用分開處理,我們處理各個轉移點時是說這個節點可以用來轉移了,然後再計算是否可以通過這個節點獲得一條新的更短的路。然後輸出時再判斷兩端的節點是否可用,如果“兩端的節點均可用”且“中間所有的轉移節點均可用”才能確定這條路能走,這是兩個獨立的過程,不能混在一起處理

// 怎麼說呢....感覺還是解釋的不是很清楚,因為這個Floyd本身是可以類似圖上DP的過程,挨個分析一步步的過程感覺很難說清楚,所以我想了四十分鐘左右也只能儘可能的給出一個合理一點的答案了,希望能理解吧(笑~)