正睿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;
}

posted @ 2020-06-07 21:46  云烟万象但过眼  阅读(76)  评论(5编辑  收藏  举报