最近學習了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; }