【洛谷P4542】 [ZJOI2011]营救皮卡丘(费用流)

洛谷

题意:
给出\(n\)个点,\(m\)条边,现在有\(k,k\leq 10\)个人从\(0\)号点出发前往\(n\)点。
规定若某个人想要到达\(x\)点,则\(1\)~\(x-1\)号点都有人到达过才行。
每条边都有对应长度,问某一个人走到\(n\)点时,所有人走的路径长度和最小为多少。

思路:

  • 直接考虑路径较为繁琐,我们可以直接考虑一个人摧毁的点集,发现点集中的点的标号是递增的。
  • \(u\)\(v,u<v\)的路径可能经过\(0\)~\(v-1\)中的任意一点,这里可以直接floyd预处理出来,那么两点之间的路径长度即可确定。
  • 考虑费用流。我们将点拆为\(i,i'\)\(i\)连向\(T\)\(S\)连向\(i'\)\(i'\)连向\(j,j>i\)。容量都为\(1\),费用只有两点之间连边时才产生费用。最后\(S\)连向\(0'\),容量为\(k\),费用为\(0\)
  • 以上建图保证每个点只会被用一次,并且最后最多\(k\)条不重叠的单调递增的路径。
  • 直接跑最小费用流即可。

其实就类似于求\(k\)个上升子序列,用流量来限制每个点只能选一次并且每个点都会选,并且选择两个点会附加费用。
代码如下:

/*
 * Author:  heyuhhh
 * Created Time:  2019/10/31 10:37:25
 */
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1005, M = 20005;

int n, m, k;

struct E {
    int from, to, cp, v;
    E() {}
    E(int f, int t, int cp, int v) : from(f), to(t), cp(cp), v(v) {}
};

struct MCMF {
    int n, m, s, t;
    vector<E> edges;
    vector<int> G[N];
    bool inq[N];
    int d[N], p[N], a[M];

    void init(int _n, int _s, int _t) {
        n = _n; s = _s; t = _t;
        for(int i = 0; i <= n; i++) G[i].clear();
        edges.clear(); m = 0;
    }

    void addedge(int from, int to, int cap, int cost) {
        edges.emplace_back(from, to, cap, cost);
        edges.emplace_back(to, from, 0, -cost);
        G[from].push_back(m++);
        G[to].push_back(m++);
    }

    bool BellmanFord(int &flow, int &cost) {
        for(int i = 0; i <= n; i++) d[i] = INF;
        memset(inq, 0, sizeof inq);
        d[s] = 0, a[s] = INF, inq[s] = true;
        queue<int> Q; Q.push(s);
        while (!Q.empty()) {
            int u = Q.front(); Q.pop();
            inq[u] = false;
            for (int& idx: G[u]) {
                E &e = edges[idx];
                if (e.cp && d[e.to] > d[u] + e.v) {
                    d[e.to] = d[u] + e.v;
                    p[e.to] = idx;
                    a[e.to] = min(a[u], e.cp);
                    if (!inq[e.to]) {
                        Q.push(e.to);
                        inq[e.to] = true;
                    }
                }
            }
        }
        if (d[t] == INF) return false;
        flow += a[t];
        cost += a[t] * d[t];
        int u = t;
        while (u != s) {
            edges[p[u]].cp -= a[t];
            edges[p[u] ^ 1].cp += a[t];
            u = edges[p[u]].from;
        }
        return true;
    }

    int go() {
        int flow = 0, cost = 0;
        while (BellmanFord(flow, cost)); 
        return cost;
    }
} MM;

int g[155][155];

void run(){
    for(int i = 0; i <= n; i++) {
        for(int j = 0; j <= n; j++) {
            g[i][j] = INF;
        }
    }
    for(int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        g[u][v] = g[v][u] = min(g[u][v], w);
    }
    int S = 2 * n + 2, T = S + 1;
    for(int k = 0; k <= n; k++) {
        for(int i = 0; i <= n; i++) {
            for(int j = 0; j <= n; j++) {
                if(k <= max(i, j)) {
                    g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
                }
            }   
        }
    }
    MM.init(T, S, T);
    for(int i = 0; i < n; i++) {
        for(int j = i + 1; j <= n; j++) {
            if(g[i][j] != INF) {
                MM.addedge(i + n + 1, j, 1, g[i][j]);
            }
        }
    }
    for(int i = 1; i <= n; i++) {
        MM.addedge(S, i + n + 1, 1, 0);
        MM.addedge(i, T, 1, 0);
    }
    MM.addedge(S, 0 + n + 1, k, 0);
    int ans = MM.go();
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    while(cin >> n >> m >> k) run();
	return 0;
}
posted @ 2019-10-31 12:55  heyuhhh  阅读(130)  评论(0编辑  收藏  举报