使用BFS求最短路
在進行圖的學習的時候大家都有學過最短路問題,其中Dijkstra算法和Floyd算法是最經典的了。可是這兩個算法在時間複雜度上有着一定的缺陷,單源最短路Dijkstra算法的時間複雜度是O(n2),任意兩點最短路Floyd算法的時間複雜度是O(n3),在圖比較小的時候這些算法是可以滿足要求的。但是也會有其他的一些比較特殊的情況,比如說所有邊權都是1的單源最短路應該如何求解呢?這當然可以使用Dijkstra來求解,但是如上所述,時間複雜度難以令人滿意。如果大家能回憶起BFS的特性,那麼從原點擴散到終點的層數就是源點到終點的最短路。
whuoj使用BFS求最短路問題6. Language of Animals
問題描述:
有n個點,m條無向邊,總共有k次查詢,每次查詢需要返回兩個點的最少的中間節點個數,如果這兩個點不能相互到達,那麼輸出-1 。n的規模是2e5,m的規模是3e5,k<=20 。
這個問題很明顯就是最短路問題,但是數據規模非常大,如果使用Dijkstra極可能會t。問題中只需要求兩個節點的中間節點個數,不存在邊權,很容易聯想到BFS求最短路。此外還有一個問題是圖的存儲問題,如果使用鄰接矩陣,那麼空間複雜度是O(n2),此時達到了4e10的空間大小,如果是int類型,單是圖所需要的空間就有16e10B,將近150GB,這肯定是不行的。考慮使用鄰接表的話,空間複雜度是O(n+2E),n是頂點數,E是邊數,最大規模也不過8e5而已。
#include <iostream>
#include <queue>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int maxn = 2e5+5;
struct anode
{
int no;
anode *next;
};
struct vnode
{
int no;
anode *first;
};
vnode G[maxn];
int bfs(vnode G[],int v,int w,int n){
int dis[maxn]={-1};
int vis[maxn]={0};
int tmp;
anode *p;
queue<int> Q;Q.push(v);vis[v]=1,dis[v]=0;
while(!Q.empty()){
tmp=Q.front();Q.pop();
p=G[tmp].first;
while(p){
if(!vis[p->no]){
Q.push(p->no);vis[p->no]=1;
dis[p->no]=dis[tmp]+1;
}
p=p->next;
}
}
/*for(int i=0;i<n;i++){
cout<<i<<" "<<dis[i]<<endl;
}*/
return dis[w];
}
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
G[i].first=NULL;
anode *pos;
int t1,t2;
for(int i=0;i<m;i++){
cin>>t1>>t2;
anode *a=(anode *)malloc(sizeof(a));
anode *b=(anode *)malloc(sizeof(b));
a->no=t1,b->no=t2;
a->next=b->next=NULL;
pos=G[t1].first;
if(!pos) G[t1].first=b;
else{
while(pos->next) pos=pos->next;
pos->next=b;
}
pos=G[t2].first;
if(!pos) G[t2].first=a;
else{
while(pos->next) pos=pos->next;
pos->next=a;
}
}
/*for(int i=0;i<n;i++){
anode *p=G[i].first;
cout<<i<<" ";
while(p){
cout<<p->no<<" ";
p=p->next;
}cout<<endl;
}*/
int k;
cin>>k;
for(int i=0;i<k;i++){
cin>>t1>>t2;
if(t1==t2) cout<<"0"<<endl;
else
cout<<bfs(G,t1,t2,n)-1<<endl;
}
return 0;
}
其中每個邊節點都有一個由鄰接節點構成的鏈表,這個鏈表的每次插入都需要申請一個鄰接節點的空間。除了結構體和結構體指針對成員的引用不同之外,由於BFS求出來的是最短路,問題要求的是中間節點個數,最後結果還要進行減1 。