【洛谷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;
}
重要的是自信,一旦有了自信,人就会赢得一切。