题意
给定一个\(N\)个点的带权无向图,你可以选择\(K\)条边,把它们的边权改为\(0\)。请最小化\(1\)到\(N\)的最短路
\(N,M\leq 5\times 10^4,K\leq 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\times K+N]\)
这个过程实际上与上面的DP是一样的
代码
#include
#include
#include
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 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\n", dis[(K + 1) * N]);
return 0;
}