最短路专题
Dijkstra
原始算法
考慮和最小生成樹Prime的相似之處
復雜度 O(V^2),不可以處理負權圖
優化方式
對于稀疏圖,算法V^2的復雜度是過高的
可以考慮在選擇最近點的時候,選擇用堆來優化
常見的實現是用優先隊列實現
復雜度約為O(E*logE)
這類變態題目不多見,可以參考吉大模板:
Floyd
三重針對點的For循環,枚舉沒一個可以松弛的操作
松弛的操作是,對于媒介節點k,嘗試能否存在 i->j 的距離 換成 i->k->j
注意不要寫反了順序,媒介節點一定是最外層循環
復雜度:O(V^3),這個算法變形應用較多,比如求兩點之間的通路中,最短的最長路徑之類的。主要是對松弛操作的理解。
SPFA
SPFA是Bellman-Ford的其中一種實現,一般都不用前者,而用SPFA。O(kE),除了個別最壞的情況外,是個很好的算法。
typedef struct{int from,to,dis; }E; int N,M,X; vector< vector<E> > map,map2;//map2是map的逆 queue<int> que; vector<bool> inQue; vector<int> dis,dis2;//dis2記錄逆圖的最短路 /*X為源點*/ map.clear(); while(!que.empty())que.pop(); inQue.clear(); dis.clear(); map.resize(N+1); inQue.resize(N+1,false); dis.resize(N+1,INF); map2.resize(N+1);//初始化map2 for(i=0;i<M;i++){scanf("%d%d%d",&e.from,&e.to,&e.dis);map[e.from].push_back(e); } que.push(X); inQue[X]=true; dis[X]=0; while(!que.empty()){w=que.front();que.pop();inQue[w]=false;for(i=0;i<map[w].size();i++){if(dis[map[w][i].to]>dis[w]+map[w][i].dis){dis[map[w][i].to]=dis[w]+map[w][i].dis;if(!inQue[map[w][i].to]){que.push(map[w][i].to);inQue[map[w][i].to]=true;}}} }負環的判斷
題目
POJ 1860?Currency Exchange
Currency Exchange 給定匯率,查找能否存在白掙錢的方案,將最短路稍改,變成最長路,判斷算法是否找到正環,即可。
#include <cstdio> #include <vector> #include <queue> using namespace std;struct E{int from,to;double r,c;E(int _from,int _to,double _r,double _c){from=_from;to=_to;r=_r;c=_c;}; };int n,m,s; double v; vector< vector<E> > map; queue<int> que; vector<bool> inQue; vector<double> dis; vector<int> rank;int main(){int i,a,b;double r_ab,c_ab,r_ba,c_ba;bool fg;while(~scanf("%d%d%d%lf",&n,&m,&s,&v)){fg=false;map.clear();dis.clear();inQue.clear();rank.clear();while(!que.empty())que.pop();map.resize(n+1);dis.resize(n+1,0);inQue.resize(n+1,false);rank.resize(n+1,0);for(i=0;i<m;i++){scanf("%d%d%lf%lf%lf%lf",&a,&b,&r_ab,&c_ab,&r_ba,&c_ba);map[a].push_back(E(a,b,r_ab,c_ab));map[b].push_back(E(b,a,r_ba,c_ba));}que.push(s);inQue[s]=true;dis[s]=v;rank[s]=1;while(!que.empty()){a=que.front();que.pop();inQue[a]=false;for(i=0;i<map[a].size();i++){if(dis[map[a][i].to]<(dis[a]-map[a][i].c)*map[a][i].r){// printf("%d -> %dn",a,map[a][i].to);dis[map[a][i].to]=(dis[a]-map[a][i].c)*map[a][i].r;if(!inQue[map[a][i].to]){rank[map[a][i].to]++;if(rank[map[a][i].to]>=n){fg=true;break;}que.push(map[a][i].to);inQue[map[a][i].to]=true;}}}if(fg){break;}}if(fg){puts("YES");}else{puts("NO");}}return 0; }POJ 3259 Wormholes
Wormholes 這個直接判斷負環……赤裸裸地。
#include <cstdio> #include <vector> #include <queue> using namespace std;struct E{int from,to,d;E(int _from,int _to,int _d){from=_from;to=_to;d=_d;}; };int n,m,s; vector< vector<E> > map; queue<int> que; vector<bool> inQue; vector<int> dis; vector<int> rank;int main(){int f,n,m,w;int i,j,s,e,t;bool fg;scanf("%d",&f);while(f--){scanf("%d%d%d",&n,&m,&w);fg=false;map.clear();dis.clear();inQue.clear();rank.clear();while(!que.empty())que.pop();map.resize(n+1);dis.resize(n+1,99999999);inQue.resize(n+1,false);rank.resize(n+1,0);for(i=0;i<m;i++){scanf("%d%d%d",&s,&e,&t);map[s].push_back(E(s,e,t));map[e].push_back(E(e,s,t));}for(i=0;i<w;i++){scanf("%d%d%d",&s,&e,&t);map[s].push_back(E(s,e,-t));}que.push(1);inQue[1]=true;dis[1]=0;rank[1]=1;while(!que.empty()){s=que.front();que.pop();inQue[s]=false;for(i=0;i<map[s].size();i++){if(dis[map[s][i].to]>dis[s]+map[s][i].d){// printf("%d -> %dn",a,map[a][i].to);dis[map[s][i].to]=dis[s]+map[s][i].d;if(!inQue[map[s][i].to]){rank[map[s][i].to]++;if(rank[map[s][i].to]>=n){//一個點入隊的次數>=n的話,那就是存在負環了。fg=true;break;}que.push(map[s][i].to);inQue[map[s][i].to]=true;}}}if(fg){break;}}if(fg){puts("YES");}else{puts("NO");}}return 0; }POJ 1062 昂貴的聘禮
這個本身是個最短路徑的問題,不過有些變化。最短路徑,有個限制,就是節點有等級差異,在某個路徑下,有些節點不可達。
為了解決這個問題,可以枚舉等級差異做。
例如酋長是x,限制為n。則枚舉:
x-n~x
x-n+1~x+1
POJ 2253 Frogger
可以用Flod做,不過方程少變化一下:
f(i,j)=min( f(i,j), max(f(i,k),f(k,j)) )
解釋:
如果需要經過第三中轉的話,最小最大的跳躍是中轉路徑中的較大者,否則跳不過去。
如果直接跳都比中轉跳短的話,何必要跳,那樣不是最小的最大跳了。
請思考,一定要最短路做嗎?當然不是,二分枚舉的效率還稍高一些:
#include <cstdio> #include <cmath> #include <cstring> #include <queue> using namespace std; #define N 205 const double esp=1e-5; double d[N][N]; int x[N],y[N],n; bool s[N]; queue<int> que;int main(){int i,j,t,cs=1;double l,r,m;while(scanf("%d",&n),n){for(i=0;i<n;i++){d[i][i]=0;scanf("%d%d",&x[i],&y[i]);for(j=0;j<i;j++){d[i][j]=d[j][i]=sqrt((x[i]-x[j])*(x[i]-x[j])+ (y[i]-y[j])*(y[i]-y[j]));}}l=0;r=d[0][1];while(r-l>esp){m=(r+l)/2;memset(s,0,sizeof(bool)*(n+1));s[0]=true;while(!que.empty())que.pop();que.push(0);while(!que.empty()){t=que.front();que.pop();for(i=0;i<n;i++){if(s[i])continue;if(d[t][i]>m)continue;s[i]=true;if(i==1)break;que.push(i);}if(s[1])break;}if(s[1]){r=m;}else{l=m;}}printf("Scenario #%dnFrog Distance = %.3fnn",cs++,m);} }POJ 1125?Stockbroker Grapevine
股票,找到一個人作為源點,使到通過這個源點到達所有人的,且最遠的那個人的距離最短。
Flod后,檢查矩陣找到那個人即可。O(V^3+V^2)。
POJ 2240 Arbitrage
Flod,后檢查自己到自己的距離是不是大于1。
注意,這個時候,就不要判斷i,j,k相等就不干的情況了!
水題報告結束。[via]
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
- 上一篇: 从数组中取出n个元素的所有组合(递归实现
- 下一篇: 高斯消元专题