题意
给定一个(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;
}