最優貿易
阿新 • • 發佈:2020-10-27
題目地址
題意
給定一個圖,找到一條從1~n的路徑,使得路徑上能選出兩個點p,q(先經過p在經過q)並且節點q的節點減去節點p的權值最大。
做法
1.先以1為起點,跑一遍spfa,求出陣列\(D[x]\)表示從節點 1 到節點 x 的所有路徑中,能過經過的權值最小的節點的權值,\(D[x]\) 與求最短路類似,只需把最短路中用 \(D[x] + w[w,y]\)更新 \(D[y]\) 改成 \(D[y] = min(D[x],price[y])\) 其中\(price[i]\) 表示第i個節點的權值,\(w[x,y]\) 表示最短路中,從x走到y的權值(此處不會用到)。
2.在以n為起點,在反向圖中,跑一遍spfa,求出陣列\(F[x]\)
3.最後列舉每個節點x,用\(F[x] - D[x]\)更新答案。
參考程式碼
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int N = 100010, M = 500010 * 4; int h[N],rh[N],e[M],ne[M],idx; int d1[N],d2[N]; int w[N]; int n,m; bool st[N]; void add(int h[],int a,int b) { e[idx] = b,ne[idx] = h[a],h[a] = idx ++; } // flag: true表示正向圖, false表示反向圖 void spfa(int h[],int dist[],int start,bool flag) { memset(st,false,sizeof st); queue<int> q; dist[start] = w[start]; q.push(start); while(q.size()) { int t = q.front();q.pop(); st[t] = false; for(int i = h[t]; ~ i; i = ne[i]) { int j = e[i]; if((flag && dist[j] > min(dist[t],w[j])) || (! flag && dist[j] < max(dist[t],w[j]))) // 滿足其中一個就行 { if(flag) dist[j] = min(dist[t],w[j]); // 根據flag的值來賦值。 else dist[j] = max(dist[t],w[j]); if(! st[j]) { st[j] = true; q.push(j); } } } } } int main() { scanf("%d%d",&n,&m); memset(h,-1,sizeof h); memset(rh,-1,sizeof rh); for(int i = 1; i <= n; i ++) scanf("%d",&w[i]); for(int i = 1; i <= m; i ++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); add(h,a,b); add(rh,b,a); // 建立反向邊 if(c == 2) add(h,b,a),add(rh,a,b); } memset(d1,0x3f,sizeof d1); // 求最小值,陣列初始化無窮大 spfa(h,d1,1,true); // 第一步求出 D[x],這裡使用d1 spfa(rh,d2,n,false); // 第二步求出 F[x],這裡使用d2 int res = 0; // 對應上面第三步 for(int i = 1; i <= n; i ++){ //cout << d1[i] << " " << d2[i] << endl; res = max(res,d2[i] - d1[i]);![](https://img2020.cnblogs.com/blog/1925085/202010/1925085-20201027210248300-164646963.png) } cout << res <<endl; }
特別說明
本題不能使用dijkstra,因為dijkstra本身就是基於貪心,第一次選取的點即為最小點,如果不是最小點,就會出錯,比如有兩個點:1和2,價格分別是2和1,一共有兩條邊:1->2,和2->1,那麼最初優先佇列中只有一個點1,此時1被彈出,它的最小价格是2,但2並不是最終的最小值。所以dijkstra演算法是不適用的。
為什麼求最大值不能在原圖跑???
因為並不是所有點都可以到達終點,必須保證,從1走到x號點的同時還要保證x能走到n號點,例如下面:
如果正向求一邊,發現最大值是 F[3] - D[3] = 6 - 1 = 5 ,但是這不滿足要求,因為3最後不能走到4,正確的答案應該是 F[4] - D[4] = 3 - 1 = 2
所以我們需要從反向圖求最大值,即4壓根都走不到3,F[3] = 0,D[3] = 6,我們在求答案的時候 res = max(res,F[3] - D[3]) 是不會更新res的,因為res初始為0。