正睿2020普转提【第六套】图
T4
题目
给你一张带边权的有向图,一共有\(N\)个点, \(M\)条边,每个点都恰好仅有一条出边连向一个节点(可以是自己,即允许自环),于是就形成了一些环下面挂着一些树一样的基环外向树(环套树)的模样。
睿爸会从这张图中的\(1\)号节点出发,一直沿着唯一的出边走,直到遇到经过的节点后停止,然后睿爸会计算一路上经过的路径和。
睿爸可以在此这张图上修改不超过\(K\)条边,但修改要符合以下条件:
1.修改后仍然是每个点仅有一条出边
2.修改的边只能在数据给你的备选边之间挑选去替换原来的边。
睿爸想知道修改不超过\(K\)条边后从\(1\)号点出发直到遇到经过的点的路径和最小值。
Solution
自环重边完全没影响。
我们设\(dis[v][tot]\)表示1号点经过\(tot\)条需替换的边到达\(v\)的最小路径和。
数据范围极小,可以直接\(dfs\)(想不到吧,这方法比标算还快)
记录\(dfs(now, sum, tot)\),表示现在在点\(now\),经过\(tot\)条需替换的边,最小路径和为\(sum\)的状态。
直接暴力转移、回溯,速度杠杠的。
\(\mathrm{Code:}\)
#include <climits>
#include <cstring>
#include <iostream>
const int N = 510, M = 2010, K = 20;
int n, m, k;
inline int read() {
int s = 0, w = 1;
char c = getchar();
while ((c < '0' || c > '9') && c != '-') c = getchar();
if (c == '-') w = -1, c = getchar();
while (c <= '9' && c >= '0')
s = (s << 1) + (s << 3) + c - '0', c = getchar();
return s * w;
}
template <class T>
inline void write(T x) {
if (x < 0) x = ~x + 1, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + 48);
return void();
}
int ans = INT_MAX;
struct Graph {
int to[M], w[M], net[M], is[M];
int fl[N], len;
inline void inc(int x, int y, int z, int l) {
to[++len] = y;
w[len] = z;
is[len] = l;
net[len] = fl[x];
fl[x] = len;
}
} G;
int dis[N][K];
int vis[N];
void dfs(int ti, int sum, int tot) {
if (tot > k) return void();
if (vis[ti]) return ans = std ::min(ans, sum), void();
if (sum >= dis[ti][tot]) return void();
vis[ti] = 1, dis[ti][tot] = sum;
for (int i = G.fl[ti]; i; i = G.net[i])
dfs(G.to[i], sum + G.w[i], tot + G.is[i]);
vis[ti] = 0;
return void();
}
signed main() {
freopen("trolley.in", "r", stdin);
freopen("trolley.out", "w", stdout);
n = read();
m = read();
k = read();
memset(dis, 0x3f, sizeof(dis));
for (int i = 1; i <= n; ++i) {
int x = read(), y = read(), z = read();
G.inc(x, y, z, 0);
}
for (int i = 1; i <= m - n; ++i) {
int x = read(), y = read(), z = read();
G.inc(x, y, z, 1);
}
dfs(1, 0, 0);
write(ans);
return 0;
}
回归正题,上面的\(dfs\)是\(ks\)的时候大佬想出来的,那么正解什么样呢?
就是一个很暴力的\(dp\ +\ 最短路\)
可笑还没\(dfs\)跑得快
总之就是烦而又琐。
代码我也写了一份
$ \mathrm{Code:}$
#include <bits/stdc++.h>
#define mmp(x, y) std ::make_pair(x, y)
#define fi first
#define se second
const int N = 510, M = 2010, K = 20;
inline int read() {
int s = 0, w = 1;
char c = getchar();
while ((c < '0' || c > '9') && c != '-') c = getchar();
if (c == '-') w = -1, c = getchar();
while (c <= '9' && c >= '0')
s = (s << 1) + (s << 3) + c - '0', c = getchar();
return s * w;
}
int n, m, k;
template <class T>
inline void write(T x) {
if (x < 0) x = ~x + 1, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + 48);
return void();
}
struct Graph {
int to[M], w[M], net[M];
int fl[N], len;
inline void inc(int x, int y, int z) {
to[++len] = y;
w[len] = z;
net[len] = fl[x];
fl[x] = len;
}
} G;
struct edge {
int x, y, z;
edge(int _x = 0, int _y = 0, int _z = 0) { x = _x, y = _y, z = _z; }
} e[M];
int f[N][N][K] = {};
int tot = 0, fl[N] = {};
int ans = INT_MAX;
void init() {
n = read();
m = read();
k = read();
memset(f, 0x3f, sizeof(f));
for (int i = 1; i <= n; ++i) {
int x = read(), y = read(), z = read();
G.inc(x, y, z);
}
for (int i = 1; i <= m - n; ++i) {
int x = read(), y = read(), z = read();
e[++tot] = edge(fl[x], y, z);
fl[x] = tot;
}
}
void work(int st, int f[N][K]) {
f[st][0] = 0;
for (int i = 0; i <= k; ++i) {
std ::priority_queue<std ::pair<int, int> > q;
for (int j = 1; j <= n; ++j) q.push(mmp(-f[j][i], j));
while (!q.empty()) {
std ::pair<int, int> u = q.top();
if (f[u.se][i] == -q.top().fi) {
int v = G.to[G.fl[u.se]];
if (v == st) v = 0;
if (f[u.se][i] + G.w[G.fl[u.se]] < f[v][i]) {
f[v][i] = f[u.se][i] + G.w[G.fl[u.se]];
q.push(mmp(-f[v][i], G.to[G.fl[u.se]]));
}
}
q.pop();
}
if (i < k) {
for (int u = 1; u <= n; ++u)
for (int j = fl[u]; j; j = e[j].x) {
int v = e[j].y;
if (v == st) v = 0;
f[v][i + 1] = std ::min(f[v][i + 1], f[u][i] + e[j].z);
}
for (int u = 1; u <= n; ++u)
f[u][i + 1] = std ::min(f[u][i + 1], f[u][i]);
}
}
}
main() {
freopen("trolley.in", "r", stdin);
freopen("trolley.out", "w", stdout);
init();
for (int i = 1; i <= n; ++i) work(i, f[i]);
for (int i = 1; i <= n; ++i)
for (int j = 0; j <= k; ++j)
ans = std ::min(ans, f[1][i][j] + f[i][0][k - j]);
write(ans);
putchar(10);
return 0;
}