題目:
題解:
最短路徑模板題:Bellman-Ford算法、Dijkstra算法、SPFA算法、Floyd-Warshall算法。
代碼如下:
class Solution {
public:
//最短路徑的練手題,哈哈,不錯,剛在複習圖算法,這就來寫
//題解1:floyd算法,時間複雜度O(n^3),三維dp,不過爲了簡化將三維dp簡化爲二維dp罷了,具體可參考《挑戰程序設計競賽》
int findTheCity_1(int n, vector<vector<int>>& edges, int distanceThreshold) {
//1、建立並初始化dp數組,若(i,j)之間存在邊,dp[i][j]則爲邊ij的權值,否則爲0x3f3f3f3f
int dp[n][n];
memset(dp,0x3f,sizeof(dp));
for(const auto& edge:edges){
dp[edge[0]][edge[1]]=dp[edge[1]][edge[0]]=edge[2];
}
//2、開始進行floyd算法求dp數組
for(int k=0;k<n;++k){
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
}
}
}
//3、求解最後結果
int res=0,minNum=INT_MAX;
for(int i=0;i<n;++i){
int count=0;
//計算i的鄰居城市中小於等於distance的城市個數
for(int j=0;j<n;++j){
if(i!=j&&dp[i][j]<=distanceThreshold){
count++;
}
}
//城市i的鄰居城市距離小於distance的城市個數更少,則更新爲更小的城市的數目。
//由於i是逐漸增大的,所以在滿足題意得要求下,我們更新更大的i爲res。
if(count<=minNum){
minNum=count;
res=i;
}
}
return res;
}
//題解2:Bellman-Ford算法,時間複雜度爲O(V^2 * E)
int findTheCity_2(int n,vector<vector<int>>& edges,int distanceThreshold){
//1、初始化dist,dist表示頂點k到達其他所有頂點的最短路徑
int dist[n];
memset(dist,0x3f,sizeof(dist));
int t=edges.size();
//2、補充有向圖爲無向圖
for(int i=0;i<t;++i){
edges.push_back({edges[i][1],edges[i][0],edges[i][2]});
}
//表示結果first,second表示小於等於閾值的最小城市數,和該城市的起始編號
pair<int,int> res(0x3f3f3f3f,n);
//3、bellman-ford算法:源點k出發的最短路徑
for(int k=0;k<n;++k){
dist[k]=0;
//由於最短路不會經過同一頂點兩次,所以最短路最多有n-1條邊,因此有些時候可以利用這個性質檢查圖是否存在負圈
for(int j=0;j<n-1;++j){
for(int i=0;i<edges.size();++i){
int a=edges[i][0],b=edges[i][1],w=edges[i][2];
//更新邊ab,終點b的最短路徑值
if(dist[a]!=0x3f3f3f3f&&dist[b]>dist[a]+w){
dist[b]=dist[a]+w;
}
}
}
//統計從頂點k出發的最短路徑小於閾值的城市個數
int count=0;
for(int i=0;i<n;++i)if(dist[i]<=distanceThreshold)count++;
//更小最小城市數和起始編號,由於k是連續變大的,所以在路徑相同時,會選用編號最大的
if(count<=res.first){
res.first=count;
res.second=k;
}
//重置dist,進行下一次搜索從k+1頂點出發的最短路
memset(dist,0x3f,sizeof(dist));
}
return res.second;
}
//題解3:dijkstra算法,時間複雜度O((V+E)*logV),數值插入和最小值取出使用小根堆,這樣來降低複雜度
int findTheCity_3(int n,vector<vector<int>>& edges,int distanceThreshold){
//1、初始化dist,dist表示從源點s出發到達其他頂點的最短路
int dist[n];
memset(dist,0x3f,sizeof(dist));
//2、構建鄰接表
map<int,set<pair<int,int>>> adjacent;
for(const auto& edge:edges){
int a=edge[0],b=edge[1],w=edge[2];
adjacent[a].insert(make_pair(b,w));
adjacent[b].insert(make_pair(a,w));
}
//res的first爲最小城市數,second爲城市編號
pair<int,int> res(0x3f3f3f3f,0);
//3、進行dijkstra算法,求源點s出發的最短路徑
for(int s=0;s<n;++s){
memset(dist,0x3f,sizeof(dist));
dist[s]=0;
//pair的first表示最短路徑,second表示源點s
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q;//小根堆
q.push(make_pair(0,s));
while(!q.empty()){
pair<int,int> p=q.top();q.pop();
int v=p.second;
if(dist[v]<p.first)continue;//取出的最小值不是最短距離,丟棄該值
for(const auto& edge:adjacent[v]){//遍歷頂點v的鄰接點,進而更新頂點v的鄰節點的最短距離
//更新頂點v的鄰接點的最短距離,並添加到q中,first爲頂點v的鄰接點,second爲邊v first的權值
if(dist[edge.first]>dist[v]+edge.second){
dist[edge.first]=dist[v]+edge.second;
q.push(make_pair(dist[edge.first],edge.first));
}
}
}
//統計從源點s出發小於等於閾值的城市數
int count=0;
for(int i=0;i<n;++i)if(dist[i]<=distanceThreshold)count++;
//更新城市數和城市編號
if(count<=res.first){
res.first=count;
res.second=s;
}
}
return res.second;
}
//題解4:spaf算法,與dijkstra算法類似,不過是bf算法的優化,使用雙端隊列存放節點
int findTheCity(int n,vector<vector<int>>& edges,int distanceThreshold){
//1、初始化dist,dist表示從源點s出發到達其他頂點的最短路,visit用來標記頂點是否被訪問
int dist[n],visit[n];
memset(dist,0x3f,sizeof(dist));
//2、構建鄰接表,first爲鄰接點頂點,second爲權值
map<int,set<pair<int,int>>> adjacent;
for(const auto& edge:edges){
int a=edge[0],b=edge[1],w=edge[2];
adjacent[a].insert(make_pair(b,w));
adjacent[b].insert(make_pair(a,w));
}
//res的first爲最小城市數,second爲城市編號
pair<int,int> res(0x3f3f3f3f,0);
//3、spaf算法:從源點s出發尋找最短路徑
for(int s=0;s<n;++s){
//初試化路徑和visit數組
memset(dist,0x3f,sizeof(dist));
memset(visit,false,sizeof(visit));
dist[s]=0;visit[s]=true;
queue<int> q;
q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
visit[u]=false;//設置頂點u不在隊列中
for(const auto edge:adjacent[u]){//遍歷頂點u的鄰接點v
int v=edge.first,w=edge.second;
if(dist[v]>dist[u]+w){//更新頂點v的最小距離
dist[v]=dist[u]+w;
if(!visit[v]){
q.push(v);
visit[v]=true;
}
}
}
}
//統計從源點s出發小於等於閾值的城市數
int count=0;
for(int i=0;i<n;++i)if(dist[i]<=distanceThreshold)count++;
//更新城市數和城市編號
if(count<=res.first){
res.first=count;
res.second=s;
}
}
return res.second;
}
};