USACO 改造路

题意

给定一个(N)个点的带权无向图,你可以选择(K)条边,把它们的边权改为(0)。请最小化(1)(N)的最短路

(N,Mleq 5 imes 10^4,Kleq 20)


解法

原来分层图还有在dinic以外的应用,涨知识了

可以很容易得到一个DP式:设(f[n][k])为到(n)号点,改变了(k)条边的边权的最短路长度

[ f[to[x]][k]=min{f[x][k]+val} \ f[to[x]][k]=min{f[x][k-1]} ]

发现(k)很小,可以直接在跑最短路时枚举(k)转移(每个点看做一个二元组)

也可以通过构造分层图的方式来解决,时空复杂度基本相同,且分层图更好理解

把原图复制(k)遍,其中,第(i)层与第(i+1)层之间连接权值为(0)的边。每向上走一层,相当于修改了一条边的边权

最后的答案即为(dis[N imes K+N])

这个过程实际上与上面的DP是一样的


代码

#include <queue>
#include <cstdio>
#include <cstring> 

using namespace std;

const int MAX_N = 4200000;

int N, M, K;

int cap;
int head[MAX_N], to[MAX_N], nxt[MAX_N], val[MAX_N];

int vis[MAX_N], dis[MAX_N];

struct node {
	int u, d;
	bool operator < (const node& rhs) const { return d > rhs.d; }
};

priority_queue<node> que;

inline void add(int x, int y, int z) {
	to[++cap] = y, nxt[cap] = head[x], head[x] = cap, val[cap] = z;
}

void Dijkstra() {
	for (int i = 1; i <= MAX_N - 10; ++i) dis[i] = 1e9;	
	
	dis[1] = 0;
	que.push((node){1, 0});
	
	while (!que.empty()) {
		int x = que.top().u; que.pop();
		if (vis[x])  continue;
		vis[x] = 1;
		for (int i = head[x]; i; i = nxt[i]) {
			if (dis[x] + val[i] < dis[to[i]]) {
				dis[to[i]] = dis[x] + val[i];
				if (!vis[to[i]])
					que.push((node){to[i], dis[to[i]]});
			}
		}
	}
	
}

int main() {
	
	scanf("%d%d%d", &N, &M, &K);
	
	int u, v, w;
	for (int i = 1; i <= M; ++i) {
		scanf("%d%d%d", &u, &v, &w);
		for (int j = 0; j <= K; ++j) {
			add(j * N + u, j * N + v, w);
			add(j * N + v, j * N + u, w);
			if (j ^ K)  
				add(j * N + u, (j + 1) * N + v, 0),
				add(j * N + v, (j + 1) * N + u, 0);						
		}
	}
	
	Dijkstra();
	
	printf("%d
", dis[(K + 1) * N]);
	
	return 0;
}
原文地址:https://www.cnblogs.com/VeniVidiVici/p/11628447.html