考研複試系列——第七節 最短路徑
前言
Floyd算法
//經過1號節點
for(i=1;i<=n;i++)//遍歷二維數組
{
for(j=1;j<=n;j++)
{
if(e[i][j] > e[i][1] + e[1][j])//判斷經過節點1是否距離更短
e[i][j] = e[i][1] + e[1][j];//更新二維數組(更新距離)
}
}
//經過2號節點
for(i=1;i<=n;i++)//遍歷二維數組
{
for(j=1;j<=n;j++)
{
if(e[i][j] > e[i][2] + e[2][j])//判斷經過節點2是否距離更短
e[i][j] = e[i][2] + e[2][j];//更新二維數組(更新距離)
}
}
//由上面的情況得到Floyd算法的核心代碼
for(k=1;k<=n;k++)//最外層循環,表示經常哪個中間節點
{
for(i=1;i<=n;i++)//遍歷二維數組
{
for(j=1;j<=n;j++)
{
if(e[i][j] > e[i][k] + e[k][j])//判斷經過節點k是否距離更短
e[i][j] = e[i][k] + e[k][j];//更新二維數組(更新距離)
}
}
}
看起來很簡單吧,但是要注意Floyd算法不能解決帶有負權值環路的問題,假如存在負權值環路,那麼每次繞一圈都減少的話,就無法找到最短路徑了。#include<iostream>
using namespace std;
int ans[101][101];//圖的鄰接矩陣
int main()
{
int N,M;
while(cin>>N>>M && N && M)
{
int i,j;
for(i=1;i<=N;i++)//鄰接矩陣初始化
{
for(j=1;j<=N;j++)
{
ans[i][j] = 999999;//表示不可達
}
ans[i][i] = 0;//城市到自己的距離爲0
}
int a,b,c;
for(i=1;i<=M;i++)//輸入城市間距離
{
cin>>a>>b>>c;
ans[a][b] = ans[b][a] = c;//注意本題是無向圖
}
int k;
for(k=1;k<=N;k++)//Floyd核心算法
for(i=1;i<=N;i++)
for(j=1;j<=N;j++)
if(ans[i][j] > ans[i][k] + ans[k][j])
ans[i][j] = ans[i][k] + ans[k][j];
cout<<ans[1][N]<<endl;//輸出結果
}
return 0;
}
Dijkstra算法
//Dijkstra算法
/*
1 將所有頂點分爲兩個部分,已知最短路徑的頂點集合P和未知最短路徑的頂點集合Q。
最開始,集合P中只有源節點一個。我們可以使用一個數組來表示對於某個頂點i,它
是否存在於集合p中,數組[i]爲1表示在,爲0表示不在。
2 用dis數組來記錄源點到各頂點的距離,初始時設源點s到自己的距離爲0,即dis[s] = 0
若存在源點能夠直接到達的頂點i,則設置dis[i] = e[s][i]。設置不可達的爲無窮
3 在集合Q的所有頂點中選擇一個離源點S最近的頂點u (即dis[u]最小)加入到集合P中,並考察
所有以點u爲起點的邊,對每一條邊執行如下操作:
如果存在一條從u到v的邊,判斷dis[u]+e[u][v]與dis[v]的大小,若前者小,則更新dis[v]爲
dis[u]+e[u][v]。
4 重複執行第三步,如果集合Q爲空,算法結束。最終dis數組中的值就是源點到所有頂點的最短路徑
*/
#include<iostream>
using namespace std;
int ans[101][101];//圖的鄰接矩陣
int book[101];//標記是否在集合P中
int dis[101];//保存距離
int main()
{
int N,M;
while(cin>>N>>M && N && M)
{
int i,j;
for(i=1;i<=N;i++)//鄰接矩陣初始化
{
for(j=1;j<=N;j++)
{
ans[i][j] = 999999;//表示不可達
}
ans[i][i] = 0;//城市到自己的距離爲0
}
int a,b,c;
for(i=1;i<=M;i++)//輸入城市間距離
{
cin>>a>>b>>c;
ans[a][b] = ans[b][a] = c;//注意本題是無向圖
}
for(i=1;i<=N;i++)//dis數組初始化
dis[i] = ans[1][i];
for(i=1;i<=N;i++)//初始化book數組
book[i] = 0;
book[1] = 1;//結點1爲源點
for(i=1;i<=N-1;i++)//Dijkstra核心算法
{
//在集合Q中尋找距離源點最近的頂點u
int min = 99999,u,v;
for(j=1;j<=N;j++)
{
if(book[j] == 0 && dis[j] < min)
{
min = dis[j];
u = j;
}
}
book[u] = 1;//將頂點u加入集合P
for(v=1;v<=N;v++)//判斷與更新dis數組
{
if(dis[v] > dis[u] + ans[u][v])
dis[v] = dis[u] + ans[u][v];
}
}
cout<<dis[N]<<endl;
}
return 0;
}
再來一道上述題目的變形:
#include<iostream>
using namespace std;
int ans[1001][1001];//保存有向圖距離
int anm[1001][1001];//保存有向圖花費
int book[1001];//標記頂點是否在集合P中
int dis[1001];//保存距離
int money[1001];//保存花費
int main()
{
int n,m;
while(cin>>n>>m && n && m)
{
int a,b,d,p;
int i,j;
for(i=1;i<=1000;i++)//距離以及花費數組的初始化
{
for(j=1;j<=1000;j++)
ans[i][j] = anm[i][j] = 99999;
ans[i][i] = anm[i][i] = 0;
}
for(i=1;i<=m;i++)//輸入數據
{
cin>>a>>b>>d>>p;
ans[a][b] = ans[b][a] = d;//注意是無向圖
anm[a][b] = anm[b][a] = p;
}
int s,t;
cin>>s>>t;//輸入起點和終點
for(i=1;i<=n;i++)//dis和money以及book數組初始化
{
dis[i] = ans[s][i];
money[i] = anm[s][i];
book[i] = 0;
}
book[s] = 1;//將源點加入到集合P
for(i=1;i<=n-1;i++)//選擇其餘n-1個節點依次加入到集合P直到集合P包含所有節點
{
int min = 999999,u,v;
for(j=1;j<=n;j++)//從集合Q中尋找一個距離源點最小的節點
{
if(book[j] == 0 && dis[j] < min)
{
min = dis[j];
u = j;
}
}
book[u] = 1;//加入節點u
for(v=1;v<=n;v++)//以u爲中轉,判斷源點到節點v的情況與更新
{
if(dis[v] > dis[u] + ans[u][v])//經過節點u距離變小了
{
dis[v] = dis[u] + ans[u][v];//更新距離
money[v] = money[u] + anm[u][v];//更新花費
}
else if(dis[v] == dis[u] + ans[u][v])//如果最小距離相等
{
if(money[v] > money[u] + ans[u][v])//選擇花費更小的
money[v] = money[u] + anm[u][v];
}
}
}
cout<<dis[t]<<" "<<money[t]<<endl;
}
return 0;
}
注意:如果對於空間有要求的話可以使用結構體來保存頂點和花費以及距離信息。