1. 程式人生 > >關於Floyd求解最小環的問題

關於Floyd求解最小環的問題

lin 自己 路徑 三個點 pos 紅色 ++i ont mem

最近學習了floyd的奇妙用處,求解最小環,自己的領悟寫在了紙上。

對於一個最小環,顯然至少要包含三個點(此處不把兩個點的回路稱之為環)

從大體上考慮的話,一定有一個點與左右兩側的點是直接連接的(即不經其他點的松弛),我們不妨設這個點為k

對於floyd,也是也k的遍歷作為松弛條件,所以考慮使用floyd求解最小環,顯然k是逐漸增大的,也就是說除去k點的那個環剩下的那條最短路中一定不能有k,

否則會出現不是環的路徑被錯誤的判定為環 ,如下圖:

技術分享

假設3已經成功的將1,2松弛,再次利用3來計算最小環時顯然此時計算出的s=dis[1][3]+e[1][3]+e[3][2];

但顯然這不是一個環啊,所以這就解釋了這個算法裏第一個for裏面i,j都只是循環到k-1的原因.

#include<bits/stdc++.h> //以hdu1599為例,切記別爆 inf*3即可
using namespace std;
#define inf 99999999
int e[105][105];
int dis[105][105];
int main()
{
int n,m,i,j,k;
while(cin>>n>>m){int a,b,c;
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
if(i==j) e[i][j]=dis[i][j]=0;
else e[i][j]=dis[i][j]=inf;
for(i=1;i<=m;++i) {
cin>>a>>b>>c;
if(c>e[a][b]) continue;
e[a][b]=e[b][a]=dis[a][b]=dis[b][a]=c;
} int ans=inf;
for(k=1;k<=n;++k)
{
for(i=1;i<k;++i)


for(j=i+1;j<k;++j)
ans=min(ans,dis[i][j]+e[i][k]+e[k][j]);
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
if(ans==inf) puts("It‘s impossible.");
else cout<<ans<<endl;
}
return 0;
}

上面說的是對於無向圖,那麽有向圖呢,也是如此嗎?顯然不成立,

對於上面代碼紅色部分,這個j之所以從i+1開始就可以了是因為無向圖的對稱性質,而有向圖並不具有這個性質,所以需要改動.

但是要是仔細想想的話,有向圖的最小環其實只要直接跑一遍floyd,然後遍歷一遍dis[i][i]即可,因為圖是無向的所以不必擔心出現重邊啊

//vjos1423為例

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
int e[210][210];
int w[210];
int main()
{
	int n,m,i,j,k;
	cin>>n>>m;
	memset(e,inf,sizeof(e));
	for(i=1;i<=n;++i) cin>>w[i];
	for(i=1;i<=m;++i){
		int a,b,c;
		cin>>a>>b>>c;
		e[a][b]=min(e[a][b],c+w[a]);
	}int ans=inf;
	 for(k=1;k<=n;++k)
	      for(i=1;i<=n;++i)
	         for(j=1;j<=n;++j)
	         	e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
  // e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
   
  // for(i=2;i<=n;++i) ans=min(ans,e[1][i]+e[i][1]);
   printf("%d\n",e[1][1]==inf?-1:e[1][1]);
	return 0;
}

關於Floyd求解最小環的問題