淺談分層圖最短路問題


分層圖最短路問題,就是把一個圖分層然后跑最短路(廢話)。

分層圖最短路問題關鍵在於怎么分層,分層通常是起到對題中某個條件的限定作用,這里我們結合例題看看。

Luogu P4568飛行路線

題意大致是給一個帶權無向圖,允許k次飛行費用為0,求最小費用。

這里就是將圖分成k層,每次從第i-1層到第i層相當於是走了一條免費的飛行路線。然后如果從第i層回到第i-1層就是一個“后悔”的過程。因此建圖方法就是每層之間正常連邊,層與層之間上到下邊權為0,下到上正常邊權。這樣直接跑Dijkstra就好了。這里分層就是對k次免費進行限制,而且連反向的邊保證程序有反悔的機會。

搬運一個大佬題解里的圖。

#include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<algorithm>
#define N 1000010
#define M 5000010
using namespace std; int head[N],nxt[M],to[M],val[M]; int n,m,cnt; void Add(int u,int v,int w) { nxt[++cnt] = head[u]; head[u] = cnt; to[cnt] = v; val[cnt] = w; return; } int k,s,t; int dis[N]; struct qwq { int u,dis; bool operator < (const qwq &a) const { return dis > a.dis; } }top,node; priority_queue<qwq>q; const int inf = 100000010; bool vis[N]; void dijkstra() { for(int i = 0;i <= n + k * n;i++) dis[i] = inf; dis[s] = 0; top.u = s; top.dis = 0; q.push(top); while(q.size()) { top = q.top(); q.pop(); int u = top.u; if(!vis[u]) { vis[u] = 1; for(int i = head[u];i;i = nxt[i]) { int v = to[i]; if(dis[v] > dis[u] + val[i]) { dis[v] = dis[u] + val[i]; node.u = v,node.dis = dis[v]; q.push(node); } } } } } int main() { scanf("%d %d %d",&n,&m,&k); scanf("%d %d",&s,&t); for(int i = 1;i <= m;i++) { int u,v,w; scanf("%d %d %d",&u,&v,&w); Add(u,v,w); Add(v,u,w); for(int i = 1;i <= k;i++) { Add(u + (i - 1) * n,v + i * n,0); Add(v + i * n,u + i * n,w); Add(v + (i - 1) * n,u + i * n,0); Add(u + i * n,v + i * n,w); } } for(int j = 1;j <= k;j++) { Add(t + (j - 1) * n,t + j * n,0); } dijkstra(); printf("%d",dis[t + k * n]); return 0; }
P4568

Luogu P1073最優貿易

題意要求我們從一個點買入,一個點賣出,獲得的費用最大,並且從1能到達n。

emmmm這個題題解里有很多玄學做法,這里只討論分層圖做法。我們暴力的想一想,每一個點都有可能買入,每一個點都有可能賣出,但是必須先買入再賣出(顯然的嘛)。所以這其實就存在一個依賴關系。這樣就可以將圖分成3層,第一層表示沒有買入和賣出,第二層表示已經買入,第三層表示已經賣出。那么每層內部正常連邊(邊權為0),層與層之間,第一層到第二層要連邊權為-a[u](u為第一層內邊的起點,a[u]為費用)表示買入,第二層到第三層連邊權為a[u]的邊表示賣出。而第二層第三層回不到第一層就是我們限制了貿易次數,只進行一次貿易。這樣圖就建好了。

仍然搬運大佬題解的圖。

#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<ctime> #include<cstdlib> #include<set> #include<queue> #include<vector> #include<string>
using namespace std; #define P system("pause");
#define A(x) cout << #x << " " << (x) << endl;
#define AA(x,y) cout << #x << " " << (x) << " " << #y << " " << (y) << endl;
#define ll long long
#define inf 1000000000
#define linf 10000000000000000
#define mem(x) memset(x,0,sizeof(x))

int read() { int x = 0,f = 1; char c = getchar(); while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') { x = (x << 3) + (x << 1) + c - '0'; c = getchar(); } return f * x; } #define N 300100
#define M 3000010
int head[N],nxt[M],to[M],val[M],dis[N],vis[N],a[N]; int n,m,cnt; void add(int u,int v,int w) { nxt[++cnt] = head[u]; head[u] = cnt; to[cnt] = v; val[cnt] = w; return; } void Add(int u,int v) { add(u,v,0); add(u + n,v + n,0); add(u + 2 * n,v + 2 * n,0); add(u,v + n,-a[u]); add(u + n,v + 2 * n,a[u]); } int s,t; void spfa() { queue<int>q; q.push(s); for(int i = 1;i <= t;i++) { dis[i] = -inf; } vis[s] = 1; dis[s] = 0; while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for(int i = head[u];i;i = nxt[i]) { int v = to[i]; if(dis[v] < dis[u] + val[i]) { dis[v] = dis[u] + val[i]; if(!vis[v]) { vis[v] = 1; q.push(v); } } } } return; } int main() { n = read(),m = read(); s = 1,t = 3 * n + 1; for(int i = 1;i <= n;i++) a[i] = read(); for(int i = 1;i <= m;i++) { int u = read(),v = read(),z = read(); Add(u,v); if(z == 2) Add(v,u); } add(n,t,0); add(3 * n,t,0); spfa(); printf("%d\n",dis[t]); }
P1073

總結一下,分層圖最短路通常是按照題中的限制關系進行分層,通過層與層間的連通與否,邊權進行限制,從而實現在跑最短路時也能兼顧限制條件。同時由於最短路算法是枚舉點進行松弛,無法保證一次就得到最優解,因此往往通過一些方法實現“反悔”操作,例題1就是通過回到原來的層實現的,而例題2則是通過spfa不斷松弛實現的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM