1. 程式人生 > >【ACM-ICPC 2018 南京賽區網路預賽】Magical Girl Haze【分層圖】

【ACM-ICPC 2018 南京賽區網路預賽】Magical Girl Haze【分層圖】

Description:

   There are NN cities in the country, and MM directional roads from uu to v(1\le u, v\le n)v(1≤u,v≤n). Every road has a distance c_ici​. Haze is a Magical Girl that lives in City 11, she can choose no more than KK roads and make their distances become 00. Now she wants to go to City NN, please help her calculate the minimum distance.

Input:

     The first line has one integer T(1 <= T <= 5), then following TT cases.

     For each test case, the first line has three integers N, MN,M and KK.

    Then the following MM lines each line has three integers, describe a road, U_i, V_i, C_iUi​,Vi​,Ci​. There might be multiple edges between uu and vv.

      It is guaranteed that N <= 100000, M <= 200000, K <= 10,
      0 ≤ Ci ​≤ 1e9. There is at least one path between City 1 and City N.

Output

      For each test case, print the minimum distance.

樣例輸入

1
5 6 1
1 2 2
1 3 4
2 4 3
3 4 1
3 5 6
4 5 2

樣例輸出

3

題意:

      給一張有向圖,給一個k,可以將圖中k條邊的邊權變為0,求從1->n的最短路徑。

解法:

      由於 k<=10,由此考慮分層圖做法。

      給每個點建10個分身,dis[i][j] 表示從 城市1 到達城市 i,一共改了 j 條邊的最短路徑。

      其餘操作和正常最短路求法一致,更新的時候有兩種更新方法。假設佇列彈出的節點是 dis[i][j],y為i可達到的點。

    則用 dis[i][j] 去更新 dis[y][j] 和 dis[y][j+1],相當於擴點操作,其餘操作和dij模板一致。

      如此操作即可求出答案。

總結:

      在圖論的問題中,分層圖問題十分常見,不單可以出現在最短路中,還可以出現在網路流、Tarjan等各類問題之中。

      重點在於題中修改 點權 或 邊權 的次數有限,因此重點在於觀察題中的資料範圍,能夠及時地想到分層圖知識。

程式碼:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int N = 1e5+100;
const int M = 5*1e5+10;
typedef long long ll;
const ll inf = 1e15;

struct Edge{
	int to,next,w;
}e[M];
int n,m,k,head[N],tot;
ll dis[N][15];
int vis[N][15];

void init()
{
	tot = 1;
	rep(i,0,n) head[i] = 0;
}

void add(int x,int y,int z)
{
	e[++tot].to = y, e[tot].next = head[x], head[x] = tot, e[tot].w = z;
}

struct Point{
	ll ans;
	int u,cnt;
}thp;

bool operator < (Point a,Point b)
{
	return a.ans > b.ans;
}

priority_queue<Point> q;

void dijstra(int s)
{
	while(q.size()) q.pop();
	thp.ans = 0, thp.u = s, thp.cnt = 0;
	q.push(thp);
	rep(i,1,n)
		rep(j,0,k) vis[i][j] = 0;
	rep(i,1,n) 
		rep(j,0,k) dis[i][j] = inf;
	dis[s][0] = 0;
	while(q.size())
	{
		thp = q.top();
		q.pop();
		int u = thp.u;
		int x = thp.cnt;
		if(vis[u][x]) continue;
		vis[u][x] = 1;
		for(int i = head[u]; i ; i = e[i].next)
		{
			int y = e[i].to;
			if(!vis[y][x]){
				if(dis[y][x] > dis[u][x]+e[i].w){
					dis[y][x] = dis[u][x] + e[i].w;
					thp.u = y, thp.ans = dis[y][x], thp.cnt = x;
					q.push(thp);
				}
			}
			if(!vis[y][x+1] && (x < k)){
				if(dis[y][x+1] > dis[u][x]){
					dis[y][x+1] = dis[u][x];
					thp.u = y, thp.ans = dis[y][x+1], thp.cnt = x+1;
					q.push(thp);
				}
			}
		} 	
	}
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		init();
		scanf("%d%d%d",&n,&m,&k);
		rep(i,1,m)
		{
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			add(x,y,z);
		}
		dijstra(1);
		printf("%lld\n",dis[n][k]);
	}	
	return 0;
}

/*
	題意:
給一張有向圖,給一個k,可以將圖中k條邊的邊權變為0,求從1->n的最短路徑
	解法:
由於 k<=10,由此考慮分層圖做法
給每個點建10個分身,dis[i][j]表示從城市1到達城市i,一共改了j條邊的最短路徑
其餘操作和正常最短路求法一致,更新的時候有兩種更新方法。假設佇列彈出的節點是dis[i][j],y為i可達到的點
則用dis[i][j]去更新dis[y][j]和dis[y][j+1],相當於擴點操作,其餘操作和dij模板一致
如此操作即可求出答案
*/