今天lyq大佬問了菜雞我一道最短路的題,結果把我問懵逼了,WC ,最短路忘乾淨了,咕咕咕,嚇得我趕忙去看了看最短路,順便水一篇博客c++
這東西是個區間dp,找了中間點來更新區間的最優值算法
沒什麼好說的就是\(3\)層循環跑,也沒什麼用數組
適用範圍:無負權迴路便可,邊權可正可負,運行一次算法便可求得任意兩點間最短路優化
時間複雜度:O(\(n^3\))這複雜度除非CCF用神威太湖之光給你跑,不然就會TLEspa
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<algorithm> #include<stack> #include<queue> using/nnamespace/nstd; int/na[101][3]; double/nf[101][101]; int/nn,i,j,k,x,y,m,s,e; int/nmain() { cin>>n; for(int/ni=1;i<=n;++i) cin>>a[i][1]>>a[i][2]; cin>>m; memset(f,0x7fffffff,sizeof(f)); for(int/ni=1;i<=m;++i) { cin>>x>>y; f[x][y]=f[y][x]=sqrt((pow(double(a[x][1]-a[y][1]),2))+(pow(double(a[x][2]-a[y][2]),2))); } cin>>s>>e; for(int/nk=1;k<=n;++k) for(int/ni=1;i<=n;++i) for(int/nj=1;j<=n;++j) if((i!=j)&&(i!=k)&&(k!=j)&&(f[i][k]+f[k][j]<f[i][j])) f[i][j]=f[i][k]+f[k][j]; cout<<f[s][e]; return/n0; }
利用對稱性,只適用於無向圖code
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<queue> #include<stack> #include<iomanip> #include<cctype> using namespace std; const int maxn=2501; long long int map[maxn][maxn]; int main() { int n,m,s,t; cin>>n>>m>>s>>t; for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) map[i][j]=0x7fffff; for(int i=1; i<=m; i++) { int x,y,z; cin>>x>>y>>z; map[x][y]=z; map[y][x]=z; } for(int k=1; k<=n; k++) for(int i=1; i<=n; i++) for(int j=1; j<=i; j++)//根據對稱性優化 map[j][i]=map[i][j]=min(map[i][j],map[i][k]+map[k][j]); printf("%lld ",map[s][t]); return 0; }
適用範圍:無負權迴路,邊權必須非負,單源最短路blog
時間複雜度:優化前O(\(n^2\))隊列
數組dis[u]表示u到s點的最短距離。ip
咱們一直找點u = min{ dis[k] , k點未訪問 },這個點就是最短路上的點,而後根據其餘點v跟u點的關係去更新下dis[v],不斷重複找和更新便可。
dis[s]=0將源點加入最短路,而後循環n-1次每次找出一個最短路上的點,找的方法是直接找出剩下的點中dis[ ]最小的那個點u,u點就是最短路上的點,而後看看其餘點v到s點的距離會不會由於
這個u點的加入而改變,即若dis[v] > dis[u] + distance[u][v] 則更新dis[v]爲 dis[u] + distance[u][v]。
#include<iostream> #include<cstdio> #include<cmath> #include<queue> #include<stack> using namespace std; const int maxn=1e9; const int hhh=105; int inq[hhh],n,m,s,x,y,z,a[105][105]; int dis[hhh]; int main() { cin>>n>>m>>s;//別忘了 !!!!!!!!!!!!!!!!!!! for(int i=1; i<=m; ++i) {//別忘了 !!!!!!!!!!!!!!!!!!! for(int j=1; j<=m; ++j) {//別忘了 !!!!!!!!!!!!!!!!!!! if(i==j) {//別忘了 !!!!!!!!!!!!!!!!!!! a[i][j]=0;//別忘了 !!!!!!!!!!!!!!!!!!! } else {//別忘了 !!!!!!!!!!!!!!!!!!! a[i][j]=maxn;//別忘了 !!!!!!!!!!!!!!!!!!! }//別忘了 !!!!!!!!!!!!!!!!!!! }//別忘了 !!!!!!!!!!!!!!!!!!! }//別忘了 !!!!!!!!!!!!!!!!!!! for(int i=1; i<=m; ++i) { cin>>x>>y>>z; a[x][y]=z; a[y][x]=z; } for(int i=1; i<=n; ++i) { dis[i]=a[s][i]; } dis[s]=0; inq[s]=1; for(int i=1; i<=n-1; ++i) { int k=0; int minn=1e9+100; for(int j=1; j<=n; ++j) { if((inq[j]==0)&&(dis[j]<minn)) { minn=dis[j]; k=j; } } if(k==0) break; inq[k]=1; for(int j=1; j<=n; ++j) { if(dis[k]+a[k][j]<dis[j]) { dis[j]=dis[k]+a[k][j]; } } } for(int i=1; i<=n; ++i) { cout<<dis[i]<<" "; } return 0; } /* 5 7 1 1 2 10 1 5 7 1 3 49 2 3 17 2 4 7 2 5 5 3 4 34 */
複雜度\(O(n*log(n))\)
利用堆(優先隊列)找最近的點,避免了循環
/* dijjstra+堆優化 */ #include<iostream> #include<cstdio> #include<algorithm> #include<string> #include<cstring> #include<cmath> #include<vector> #include<queue> #include<stack> using namespace std; const int maxn=520; const int INF=1e9; vector<pair<int,int> >/*edge*/e[maxn];//定義一個二維的動態數組以便之後創建鄰接表 int dis[maxn],inq[maxn]/*in_queue*/; int n,m,s,t; void start() { for(int i=0; i<maxn; ++i) { e[i].clear() ; }//若是不是多組數據能夠沒有 memset(inq,0,sizeof(inq)); for(int i=0; i<maxn; ++i) { dis[i]=INF; } //初始化dis } int main() { std::ios::sync_with_stdio(false);//cin優化一下下(主要是我懶不想打scnaf) while(cin>>n>>m) { start();//初始化 for(int i=0; i<m; ++i) { int x,y,z;//雙向道路的起點、終點 、權值 cin>>x>>y>>z; e[x].push_back(make_pair(y,z));//把x連到y上權值是z e[y].push_back(make_pair(x,z));//雙向的緣由 } int s,t; cin>>s>>t; //queue<int>q; priority_queue< pair<int,int> >q; //q.push(s);//將lyq家(起點入隊相似於bfs) q.push(make_pair(-dis[s],s));//pushu(-d[s])變成小根堆 ,其實也能夠直接定義(不過我懶) dis[s]=0;//從起點到起點必定距離爲0 inq[s]=1;//標記一下s點入隊 /*bfs階段*/ while(!q.empty() ) { //int now=q.front() ;//now 是如今lyq到的點 int now=q.top().second;//只須要第二個就ok q.pop();//吃隊 inq[now]=0;//更新inq for(int i=0; i<e[now].size(); ++i) {//遍歷 int v=e[now][i].first; /*鬆弛操做*/ if(dis[v]>dis[now]+e[now][i].second) { dis[v]=dis[now]+e[now][i].second; if(inq[v]==1) continue;//若是在隊列裏就無論了 else { inq[v]=1;//標記進隊 //q.push(v); q.push(make_pair(-dis[v],v)); } } } } if(dis[t]==1e9) { cout<<"-1\n";//若是最短路仍是1e9那lyq就找不到了.... } else { cout<<dis[t]; } } return 0; }
就和bfs差很少,本身看代碼吧
#include<bits/stdc++.h> using namespace std; const int maxn=99999999; const int h=1005; int dis[h],a[h][h],inq[h],pre[h]; int n,m,x,y,z,s; queue<int>q; int main() { cin>>n>>m>>s;//別忘了 !!!!!!!!!!!!!!!!!!! for(int i=1; i<=m; ++i) {//別忘了 !!!!!!!!!!!!!!!!!!! for(int j=1; j<=m; ++j) {//別忘了 !!!!!!!!!!!!!!!!!!! if(i==j) {//別忘了 !!!!!!!!!!!!!!!!!!! a[i][j]=0;//別忘了 !!!!!!!!!!!!!!!!!!! } else {//別忘了 !!!!!!!!!!!!!!!!!!! a[i][j]=maxn;//別忘了 !!!!!!!!!!!!!!!!!!! }//別忘了 !!!!!!!!!!!!!!!!!!! }//別忘了 !!!!!!!!!!!!!!!!!!! }//別忘了 !!!!!!!!!!!!!!!!!!! for(int i=1; i<=m; ++i) { dis[i]=maxn; } for(int i=1; i<=m; ++i) { cin>>x>>y>>z; a[x][y]=z; a[y][x]=z; } inq[s]=1; dis[s]=0; q.push(s); while(!q.empty()) { int k=q.front() ; q.pop(); inq[k]=0; for(int i=1; i<=n; ++i) { if(a[k][i]!=maxn) { if(dis[k]+a[k][i]<dis[i]) { dis[i]=dis[k]+a[k][i]; if(!inq[i]) { q.push(i); inq[i]=1; } } } } } for(int i=1; i<=n; ++i) { cout<<dis[i]<<" "; } return 0; }
/* 原隊列改成雙端隊列,對一個要加入隊列的點u,若是dis[u] 小 於隊首元素v的dis[v],那麼就就加入到隊首元素,不然加入到隊尾。 */ #include<bits/stdc++.h> using namespace std; const int maxn=99999999; const int h=2505; long long int dis[h],a[h][h],inq[h],pre[h]; int n,m,x,y,z,s; deque<int>q; int main() { cin>>n>>m;//別忘了 !!!!!!!!!!!!!!!!!!! for(int i=1; i<=m; ++i) {//別忘了 !!!!!!!!!!!!!!!!!!! for(int j=1; j<=m; ++j) {//別忘了 !!!!!!!!!!!!!!!!!!! if(i==j) {//別忘了 !!!!!!!!!!!!!!!!!!! a[i][j]=0;//別忘了 !!!!!!!!!!!!!!!!!!! } else {//別忘了 !!!!!!!!!!!!!!!!!!! a[i][j]=maxn;//別忘了 !!!!!!!!!!!!!!!!!!! }//別忘了 !!!!!!!!!!!!!!!!!!! }//別忘了 !!!!!!!!!!!!!!!!!!! }//別忘了 !!!!!!!!!!!!!!!!!!! for(int i=1; i<=m; ++i) { dis[i]=maxn; } for(int i=1; i<=m; ++i) { cin>>x>>y>>z; a[x][y]=z; a[y][x]=z; } s=1; inq[s]=1; dis[s]=0; q.push_front(s); while(!q.empty()) { int k=q.front() ; q.pop_front() ; inq[k]=0; for(int i=1; i<=n; ++i) { if(a[k][i]!=maxn) { if(dis[k]+a[k][i]<dis[i]) { dis[i]=dis[k]+a[k][i]; if(!inq[i]) { if(dis[i]>dis[q.front()]) q.push_front(i); else q.push_back(i); inq[i]=1; } } } } } cout<<dis[n]; return 0; }
int flag=0; int dis[N]= {}; int vis[N]= {}; void Clr() { memset(dis,0,sizeof(dis)); memset(vis,0,sizeof(vis)); memset(first,0,sizeof(first)); cnt=1; flag=0; } void SPFA(int u) { vis[u]=1; for(int i=first[u]; i; i=e[i].nxt) { int v=e[i].v; if(dis[u]+e[i].w<dis[v]) { if(vis[v]||flag) { flag=1; break; } dis[v]x=dis[u]+e[i].w; SPFA(v); } } vis[u]=0; } // for(int i=1; i<=n; i++) { SPFA(i); if(flag)break; } /*第二種*/ /* bool spfa(int u){ vis[u] = true; int i; for(i = head[u]; i; i = e[i].next){ int v = e[i].to, w = e[i].w; if(dis[v] > dis[u] + w){ dis[v] = dis[u] + w; if(vis[v]) return false; if(!spfa(v)) return false; } } vis[u] = false; return true; } */
/* 對每一個要出對的元素u,比較dis[u]和隊列中dis的平均值,若是dis[u]更大, 那麼將它彈出放到隊尾,取隊首元素在進行重複判斷,直至存在dis[x]小於平均值 */ #include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn = 205; int n, dis[maxn], sum = 0, cnt = 0; int G[maxn][maxn] = {}; bool inq[maxn] = {}; int main() { memset(dis, 0x3f, sizeof(dis)); cin >> n; for (int i = 1; i < n; ++i) { for (int j = i + 1; j <= n; ++j) { cin >> G[i][j]; } } queue<int> Q; Q.push(1); inq[1] = true; dis[1] = 0; cnt = 1; while (!Q.empty()) { int u = Q.front(); while (dis[u]*cnt > sum) { Q.pop(); Q.push(u); u = Q.front(); } Q.pop(); cnt--; sum -= dis[u]; inq[u] = false; for (int i = u + 1; i <= n; ++i) { if (dis[i] > dis[u] + G[u][i]) { dis[i] = dis[u] + G[u][i]; if (!inq[i]) { Q.push(i); sum += dis[i]; cnt++; } } } } cout << dis[n]; }
將全部的邊方向取反,求從1號點到全部點的單源最短路便可。
新建一個超級源點,向全部起點連一條邊權爲0的邊,從它開始跑單源最短路。
在進行dijkstra算法時,額外維護一個sum數組表示到達某個點的最短路條數。在進行鬆弛操做時維護sum數組。
在dijkstra算法中額外記錄一個數組from,表示從1號點到達它的最短路是從哪一個點走過來的。
最後從n號點不斷沿着from走回去便可。