一、多元最短路求法
多元都求出來了,單源的肯定也能求。
思想是動態規劃的思想:從任意節點A到任意節點B的最短路徑不外乎2種可能,1是直接從A到B,2是從A經過若干個節點X到B。所以,我們假設Dis(AB)為節點A到節點B的最短路徑的距離,對于每一個節點X,我們易寫出狀態轉移方程Dis(AB) =min(Dis(AX) + Dis(XB) ,Dis(AB))這樣一來,當我們遍歷完所有節點X,Dis(AB)中記錄的便是A到B的最短路徑的距離。
memset(Dis,0x3f,sizeof(Dis);
//初始化,這里采用0x3f而非0x7f,是當兩個0x7f7f7f7f相加符號變號成為一個無窮小量。
void floyd(int N)
{int i,j,k;for(k=0;k<N;k++){for(i=0;i<N;i++){for(j=0;j<N;j++){if(Dis[i][k]+Dis[k][j]<Dis[i][j]){Dis[i][j]=Dis[i][k]+Dis[k][j];}}}}
}
這里一定要把K寫到外邊,需要先更新K前面的點在更新K后的點才有意義。
結合代碼 并參照上圖所示 我們來模擬執行下 這樣才能加深理解:
第一關鍵步驟:當k執行到x,i=v,j=u時,計算出v到u的最短路徑要通過x,此時v、u聯通了。
第二關鍵步驟:當k執行到u,i=v,j=y,此時計算出v到y的最短路徑的最短路徑為v到u,再到y(此時v到u的最短路徑上一步我們已經計算過來,直接利用上步結果)。
第三關鍵步驟:當k執行到y時,i=v,j=w,此時計算出最短路徑為v到y(此時v到y的最短路徑長在第二步我們已經計算出來了),再從y到w。
依次掃描每一點(k),并以該點作為中介點,計算出通過k點的其他任意兩點(i,j)的最短距離,這就是floyd算法的精髓!同時也解釋了為什么k點這個中介點要放在最外層循環的原因.
完整代碼:
#include<iostream>
#include<stack>
using namespace std;
#define MAX 1000
int Graph[MAX][MAX];
int Dis[MAX][MAX];
#define infinite 1000
int path[MAX][MAX];void floyd(int N)
{int i,j,k;for(k=0;k<N;k++){for(i=0;i<N;i++){for(j=0;j<N;j++){if(Dis[i][k]+Dis[k][j]<Dis[i][j]){Dis[i][j]=Dis[i][k]+Dis[k][j];path[i][j]=k;}}}}}void print_path(int N)
{int i,j;for(i=0;i<N;i++){for(j=0;j<N;j++){if((i!=j) &&Dis[i][j]!=infinite){cout<<i+1<<"----"<<j+1<<" distance:"<<Dis[i][j]<<endl;cout<<"path:"<<endl;int k=j;stack <int> ph;do{k=path[i][k];ph.push(k);}while(k!=i);cout<<ph.top()+1;ph.pop();while(!ph.empty()){cout<<"->"<<ph.top()+1;ph.pop();}cout<<"->"<<j+1<<endl;}}}
}void main()
{int N,i,j;cin>>N;for(i=0;i<N;i++){for(j=0;j<N;j++){int g;cin>>g;Graph[i][j]=g;Dis[i][j]=g;}}
//初始化路徑for(i=0;i<N;i++){for(j=0;j<N;j++){path[i][j]=i;}}floyd(N);print_path(N);system("pause");
}
二、連通性
講Dis[i][j]不連聯通時設置為0,聯通時設置為1.
則可得狀態轉移方程
dis[i][j]=dp[i][j]||(dp[i][k]&&dp[k][j]);
跟上面代碼除了狀態轉移方程之外還有初始化不同,這個都初始化為0;
其余都一樣。要么ij直接連通,要么ij通過K聯通。
void floyd(int N)
{int i,j,k;for(k=0;k<N;k++){for(i=0;i<N;i++){for(j=0;j<N;j++){if((dp[i][k]&&dp[k][j])&&!Dis[i][j]){Dis[i][j]=Dis[i][k]+Dis[k][j];path[i][j]=k;}}}}
}
三、求無向圖中可以刪除一些邊,使得任意兩點的最短路不改變,求這些邊能刪除的最大的條數。(最小生成樹問題)
首先先在輸入邊的時候將重邊去掉,保留最小的。
然后進行佛洛依德。
如果原來兩點的最短距離大于經過第三個點的最短距離的話,那么我們就將這兩點的最短距離
替換成經過第三條邊的最短距離,當循環節結束后通過對比兩點之間的距離變化,即可知哪些邊將被刪去。但是~~~當兩點之間本來沒有邊的情況下,我們肯定是經過第三個點所到達的。那么就沒有替換原來的邊,這種情況的話,就直接continue;
四、無向圖最小環
若用dis[i][j]表示ij之間的最小值,則由i j 加線外一點k的環值為dis[i][j]+length[i][k]+length[k][j];
枚舉中間點k,在用其更新最短路前,先找最小環,令1<=i<j<k,即k點必定不在i,j的最短路上,則這個環中至少有三個點,可得狀態轉移方程 ans=min(ans,dis[i][j]+length[i][k]+length[k][j]);
#include <cstdio>
#include <cstring>
#include <map>
#include <queue>
#include <algorithm>using namespace std;struct Node {int s[9];//s數組表示包括本端所連的fenceNode() {memset(s,0,sizeof(s));}bool operator < (const Node& a) const {for(int i=0;i<9;++i)if(s[i]<a.s[i])return true;else if(s[i]>a.s[i])return false;return false;
}bool operator ==(const Node& a) const {for(int i=0;i<9;++i)if(s[i]!=a.s[i])return false;return true;
}}fence[205];
int n,s,ls,ns,n1s,n2s,sta,des,cur;
int g[105][105],cnt=0,dis[105][105];
bool vis[105];
map<Node,int> mp;
int floyd() {int ans=0x1f1f1f1f;for(int i=1;i<=n;++i)for(int j=i;j<=n;++j)dis[i][j]=dis[j][i]=g[i][j];for(int k=1;k<=cnt;++k) {for(int i=1;i<k;++i)//尋找最小環for(int j=i+1;j<k;++j)if(dis[i][j]+g[i][k]+g[k][j]<ans)//由于此處會存在三個INF相加,所以INF設為0x1f1f1f1fans=dis[i][j]+g[i][k]+g[k][j]; for(int i=1;i<=n;++i)//更新最短路for(int j=1;j<=n;++j)if(dis[i][j]>dis[i][k]+dis[k][j])dis[i][j]=dis[i][k]+dis[k][j];
}
return ans;
}
int main() {//freopen("fence6.in","r",stdin);// freopen("fence6.out","w",stdout);memset(g,0x1f,sizeof(g));scanf("%d",&n);for(int i=1;i<=n;++i) {//讀入邊數據,并給每個點標一個數scanf("%d%d%d%d",&s,&ls,&n1s,&n2s);fence[i<<1].s[8]=fence[(i<<1)|1].s[8]=s;while(n1s-->0)scanf("%d",&fence[i<<1].s[n1s]);sort(fence[i<<1].s,fence[i<<1].s+9);if(mp[fence[i<<1]]==0)mp[fence[i<<1]]=++cnt;while(n2s-->0)scanf("%d",&fence[(i<<1)|1].s[n2s]);sort(fence[(i<<1)|1].s,fence[(i<<1)|1].s+9);if(mp[fence[(i<<1)|1]]==0)mp[fence[(i<<1)|1]]=++cnt;sta=mp[fence[i<<1]];des=mp[fence[(i<<1)|1]];g[sta][des]=g[des][sta]=ls;//邊信息轉成點信息
}
printf("%d\n",floyd());
return 0;
}
五、傳遞閉包問題
鄰接矩陣是顯示兩點的直接關系,如a直接能到b,就為1。而傳遞閉包顯示的是傳遞關系,如a不能直接到c,卻可以通過a到b到d再到c,因此a到c為1。
另外矩陣A進行自乘即A{2}得到的矩陣中,為1的值表示走最多兩步可以到達。A{3}矩陣中為1的值表示,最多走三步可以到達。
簡單來說,就是有向圖確定先后順序。
/*
題目:n頭牛進行m場比賽,問能確定排名的有多少頭牛。解答:構造一個n個點的有向圖,如果牛a勝b,那么a->b,如果a->b,b->c,則有a->c,這個用floyd。最后得到該圖的傳遞閉包link的二維數組。最后統計每一個點入度和出度和為n-1的點的個數即可。
*/
#include<stdio.h>
#include<string.h>
const int MAX=105;
/*
有向圖的傳遞閉包!
注意傳遞之前一定要初始化!
如果i!=j&&(i,j)不屬于E(邊的集合) t[i][j]=0;
如果i=j||(i,j)屬于E(邊的集合) t[i][j]=1;
*///傳遞閉包
void Transitive_Closure(int n,bool t[][MAX])
{int i,j,k;for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)t[i][j]=t[i][j]|(t[i][k]&t[k][j]);
}
int main()
{int n,i,j,m,st,ed,sum,num;bool t[MAX][MAX];while(scanf("%d%d",&n,&m)){if(n==0&&m==0)return 0;memset(t,false,sizeof(t));for(i=1;i<=n;i++)t[i][i]=true;for(i=1;i<=m;i++){scanf("%d%d",&st,&ed);t[st][ed]=true;}//上面的代碼都是初始化Transitive_Closure(n,t);sum=0;for(i=1;i<=n;i++){num=0;for(j=1;j<=n;j++)if(i==j)continue;elsenum+=(t[i][j]||t[j][i]);//統計出度和入度的個數!sum+=(num==n-1);}printf("%d\n",sum);}return 0;
}
/*
5 5
4 3
4 2
3 2
1 2
2 52
*/
總結
以上是生活随笔為你收集整理的Floyd —Warshall(最短路及其他用法详解)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。