分層圖最短路

分層圖最短路

定義:

顧名思義,「分層圖最短路」就是在多層平行的圖上跑最短路node

模型:

分層圖最短路的模型就是在最短路模型的基礎上加上k個決策c++

最短路模型:給定n個點m個條路,求從s出發到t的最短距離數組

分層圖最短路模型:給定n個點m條路以及k個決策,再求出s到t的最短距離學習

k個決策不會影響圖的結構,只會影響當前的代價或狀態spa

(PS:對於每一道題,決策的具體內容是不同的,能夠結合以後的例題理解).net


模板:

面對分層圖最短路這種題,咱們通常有兩種方法解決:code

  1. 直接構建k+1層平行的圖blog

  2. 多開一維記錄決策信息get

  • 如今來說第一種方法
  1. 面對每一層,咱們仍是先像普通最短路同樣連邊建圖博客

  2. 處理層與層,咱們將有邊相連的兩個點u、v各自多向下一層連一條邊(權值視題目的決策內容而定,而且是有向邊,向下的有向邊,這樣就模擬出了k次決策!

  3. 當有n個點時,(1 ~ n)表示第一層,(1+n)~(n+n)爲第二層,(1+2 * n)~(n+2 * n)爲第三層·······(1+i * n)~(n+i * n)爲第i+1層

  4. 由第3點可得:由於要建k+1層圖,因此數組要開到n * ( k + 1),點的個數也爲n * ( k + 1 )

有點抽象,咱們來舉個栗子:
n=4,m=3,k=2
0  1  100
1  2  100
2  3  100

建成的k+1層圖以下:


如今給出完整的模板code:

#include <bits/stdc++.h>
using namespace std;
int n,m,k,u,v,w,s,t,tot;
int dis[5000010],vis[5000010],head[5000010];  //根據題意改變數組大小 
priority_queue<pair<int,int> > shan;

struct node {
	int to,net,val;
}e[5000010];

inline void add(int u,int v,int w) {  //鏈式前向星存邊 
	e[++tot].val=w;
	e[tot].to=v;
	e[tot].net=head[u];
	head[u]=tot;
}

inline void dijkstra(int s) {  //Dijkstra跑最短路的板子 
	memset(dis,0x3f,sizeof dis);
	dis[s]=0;
	shan.push(make_pair(0,s));
	while(!shan.empty()) {
		int x=shan.top().second;
		shan.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(register int i=head[x];i;i=e[i].net) {
			int v=e[i].to;
			if(dis[v]>dis[x]+e[i].val) {
				dis[v]=dis[x]+e[i].val;
				shan.push(make_pair(-dis[v],v));
			}
		}
	}
}

int main() {
	scanf("%d%d%d",&n,&m,&k);
	scanf("%d%d",&s,&t);
	for(register int i=1;i<=m;i++) {
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);  //正常存邊 
		add(v,u,w);
		for(register int j=1;j<=k;j++) {  //構建以後的k層圖 
			add(u+j*n,v+j*n,w);  //每一層的連邊同上正常連邊 
			add(v+j*n,u+j*n,w);
			add(u+(j-1)*n,v+j*n,0);  //層與層之間的聯繫 
			add(v+(j-1)*n,u+j*n,0);  //層與層邊的權值不必定是0!要視題目而定 
		}
	}
	for(register int i=1;i<=k;i++) {  //將每一層的終點特別連起來 
		add(t+(i-1)*n,t+i*n,0);
	}
	dijkstra(s);
	printf("%d",dis[t+k*n]);  //最終答案存在最後一層的終點處 
	return 0;
}
  • 第二種作法

由於我認爲第一種作法比較簡單,就沒怎麼編寫第二種作法的代碼(並且兩種作法面對不卡數據的題目選任意一種都能過)

因此如今就給出我學習的博客,你們能夠看這個連接自行學習第二種作法(我的感受有點相似於DP思想)

PS:個人第一種作法代碼和上面的博客有些許的區別,但願你們區分開來,不要記混了qwq


例題:

  1. 這道題徹底就是分層圖最短路題型的模板題

  2. 注意一點就是這題的編號是從0~(n-1)的,因此爲了處理方便,咱們在輸入後就進行加一操做,轉換爲1~n的編號

  3. 如今給出主程序代碼(Dijkstra部分見上面的模板):

int main() {
	scanf("%lld%lld%lld",&n,&m,&k);
	scanf("%lld%lld",&s,&t);
	s++;t++;  //由於編號從0開始,方便處理都加1,下面的u++、v++同理 
	for(register long long i=1;i<=m;i++) {
		scanf("%lld%lld%lld",&u,&v,&w);
		u++;v++;
		add(u,v,w);
		add(v,u,w);
		for(register long long j=1;j<=k;j++) { 
			add(u+j*n,v+j*n,w);
			add(v+j*n,u+j*n,w);
			add(u+(j-1)*n,v+j*n,0);
			add(v+(j-1)*n,u+j*n,0);  //由於該題的決策時免費,因此權值爲0 
		}
	}
	for(register long long i=1;i<=k;i++) {
		add(t+(i-1)*n,t+i*n,0);
	}
	dijkstra(s);
	printf("%lld",dis[t+k*n]);
	return 0;
}
  1. 這題也是直接套模板就能A掉的,並且規定了起點是1終點是n,雙倍經驗get!

  2. 哦哦哦,補充一下,題意就是求從1到n的最短路距離,不是輸出對哪些小徑進行升級ovo!

  3. 直接給主程序代碼:

int main() {
	scanf("%d%d%d",&n,&m,&k);
	for(register int i=1;i<=m;i++) {
		scanf("%d%d%d",&u,&v,&t);
		add(u,v,t);
		add(v,u,t);
		for(register int j=1;j<=k;j++) {
			add(u+j*n,v+j*n,t);
			add(v+j*n,u+j*n,t);
			add(u+(j-1)*n,v+j*n,0);
			add(v+(j-1)*n,u+j*n,0);
		}
	}
	for(register int i=1;i<=k;i++) {
		add(i*n,(i+1)*n,0);  //由於每一層的終點就是n,因此改寫成這樣,注意一下區別ovo 
	}
	dijkstra();
	printf("%d",dis[(k+1)*n]);
	return 0;
}
  1. 這道題95%都是板子,只有一點不一樣:本題的決策內容是花費減半,因此層與層之間的權值再也不是0,而是這條邊本來權值的一半!

  2. 其餘的就沒什麼好說的,三倍經驗get!

  3. 給出主程序代碼以下:

int main() {
	scanf("%d%d%d",&n,&m,&k);
	for(register int i=1;i<=m;i++) {
		scanf("%d%d%d",&u,&v,&t);
		add(u,v,t);
		add(v,u,t);
		for(register int j=1;j<=k;j++) {
			add(u+j*n,v+j*n,t);
			add(v+j*n,u+j*n,t);
			add(u+(j-1)*n,v+j*n,t/2);   //注意區別哦!這裏的權值再也不是0,而是一半的花費! 
			add(v+(j-1)*n,u+j*n,t/2);
		}
	}
	for(register int i=1;i<=k;i++) {
		add(i*n,(i+1)*n,0);   //同上一道題,由於每一層的終點就是n,因此改寫成這樣
	}
	dijkstra();
	printf("%d",dis[(k+1)*n]);
	return 0;
}
  1. 初看這道題容易直接當作純板子題,可是你會發現程序過不了樣例:答案是4,本身的輸出是5

  2. 再去讀題,請注意這句話:「總費用決定於其中最長的電話線的長度」,說明不是求從1到n的最短路,而是求從1到n的路徑中最大邊權最小,因此咱們須要改一下Dijkstra的入隊判斷:

if(dis[v]>max(dis[x],e[i].val)) {
   dis[v]=max(dis[x],e[i].val);
   shan.push(make_pair(-dis[v],v));
}
  1. 而後不要忘記有輸出-1這種狀況,因此咱們須要在輸出前加一個判斷:
if(dis[(k+1)*n]>1000001) printf("-1");
else printf("%d",dis[(k+1)*n]);

由於每條路的邊權值不會超過1000000(題目規定)
  1. 剩下的跟以上題目代碼同樣,就不給出代碼了,四倍經驗get!(注意一下,這道題的決策內容也是免費,因此層與層的邊權值爲0便可

最後,以上只是我對於「分層圖最短路」的基本學習記錄,有任何理解錯誤的地方,還煩請各位dalao指出,蒟蒻感激涕零啊orz!

相關文章
相關標籤/搜索