最短路徑
dijkstra
不可解決有負權的圖
但是若是沒有負權的話,最好用這個,而不要用spfa,因為spfa的時間復雜度極其不穩定
對於稀疏圖來說,spfa的時間復雜度確實是要比dijkstra要低
但,若是稠密圖的話,spfa的時間復雜度就極其不可觀了
所以,dijkstra的重要性顯然
1.定義
不可解決有負權邊的圖
時間復雜度為O(n2)
經過堆優化的dijkstra的時間復雜度為O(nlogn)
2.算法描述
貪心的思想(這是因為這個思想,所以判不出來負權邊--(咳咳,個人之見,逃~)
以起始點為中心向外層層擴展(廣搜的思想),直到擴展到終點為止
基本思想:
引入兩個集合S和U。
S:記錄已求出最短路徑的頂點
U:記錄未求出最短路徑的頂點
(能到源點的所有點)
操作:
1.初始時,S集合中只包含源點s
U集合中包含除s以外的其他頂點 且 U中頂點的距離為“起點s到該頂點的距離”
(若相連,即為邊權值;若不相連,則設其為無窮大)
2.從U中選出“距離最短的頂點k”,將k加入s中,並將k從U集合中移出
3.用新加入的頂點k來更新U集合中各個頂點到起點s的距離
之所以更新U中頂點的距離,是因為 上一步中確定了k是求出最短路徑的頂點,從而可以用k來更新其他的值
4.重復2.3,直到遍歷完所有點
(假裝已經會了)
普普通通的dijkstra
#include <iostream> #include <algorithm> #include <cmath> #include <cstdio> #include <cstring> #include <cstdlib> using namespace std; int map[110][110];//這就是map數組,存儲圖 int dis[10010];//dis數組,存儲估計值 int book[10010];//book[i]代表這個點有沒有被當做源點去搜索過,1為有,0為沒有。這樣就不會重復搜索了。 int n,m; void dijkstra(int u)//主函數,參數是源點編號 { memset(dis,88,sizeof(dis));//把dis數組附最大值(88不是十進制的88,其實很大) int start=u;//先從源點搜索 book[start]=1;//標記源點已經搜索過 for(int i=1; i<=n; i++) { dis[i]=min(dis[i],map[start][i]);//先更新一遍 } for(int i=1; i<=n-1; i++)//源點不用 { int minn=9999999;//這里的minn不是題解上的minn,這代表的是最近點到源點的距離,start才代表最近的點、 for(int j=1; j<=n; j++) if(book[j]==0 && minn>dis[j]) { minn=dis[j]; start=j;//找到離源點最近的點,然后把編號記錄下來,用於搜索。 } book[start]=1; for(int j=1; j<=n; j++) dis[j]=min(dis[j],dis[start]+map[start][j]);//以新的點來更新dis。 } } int main() { cin>>n>>m; memset(map,88,sizeof(map)); for(int i=1; i<=m; i++) { int a,b,c; cin>>a>>b>>c; map[a][b]=c; }//有向圖吧 for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(i==j) map[i][j]=0;//僅是初始化 dijkstra(1);//以1為源點。 for(int i=1; i<=n; i++) cout<<dis[i]<<" "; }
(普普通通的dij也幾乎過不了什么,還得加上一個鏈式前向星)
堆優化的dijkstra
1.基本思想
//堆優化dijkstra 邊權不可為負
#include<cstdio> #include<algorithm> #include<queue> #include<cstring>
using namespace std; inline int read()//快讀
{ int sum = 0,p = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '0') p = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (sum *= 10) += ch - '0'; ch = getchar(); } return sum * p; } const int maxn = 1e5 + 5; const int maxm = 2e5 + 5; int n,m,s;//n為點的個數,m為變得個數,s為源點
int tot,dis[maxn],wei[maxm];//dis為i點到源點的距離,wei為邊權
int head[maxn],to[maxm],nxt[maxm];//鏈式前向星
bool mrk[maxn];//標記該點是否被加入到最短路徑的生成樹中(0-沒有,1-有)
priority_queue< pair<int,int> > q; /* 優先隊列:默認是大根堆 pair第一維為dis[i]的相反數(這樣就可以變成小根堆啦) 第二維為節點編號 */
void dijkstra() { memset(dis,0x3f,sizeof(dis)); dis[s] = 0;//dis初始化 起點為0,其余為正無窮
q.push(make_pair(0,s)); while(q.size()) { int x = q.top().second; q.pop();//取堆頂
if(mrk[x]) continue; mrk[x] = true; for(int i = head[x];i;i = nxt[i]) {//掃描所有出邊
int y = to[i]; int z = wei[i]; if(dis[y] > dis[x] + z) { dis[y] = dis[x] + z; q.push(make_pair(-dis[y],y)); } } } } int main() { n = read(),m = read(),s = read(); int x,y,z; for(int i = 1;i <= m;i++) { x = read(),y = read(),z = read(); wei[++tot] = z; nxt[tot] = head[x]; to[tot] = y; head[x] = tot; }//構建鄰接矩陣 (通常可用add函數)
dijkstra(); for(int i = 1;i <= n;i++) printf("%d ",dis[i]); return 0; }
(我應該會回來補一補的qwq...)