P1674 [USACO05FEB] Secret Milking Machine G 题解
WilliamFranklin · · 题解
这道题较水,就是一个二分+最大流
主要思路
看到求这种最大最小值的,第一直觉就是——二分。
简单证明一下,发现的确可以。
然后根据之前做题的经验,把边权小于等于
设源点为
但是,这里有一个问题:在题目中,要求一条路只能走一次,但在我们新建的有向图中,无向图中的边都已转化成两条边,所以相当于每一条路都有可能走两边。
其实我们不用担心,因为在网络流中,正向流一次,再反向流一次后,就相当于没流,抵消了。
进一步,我们发现,在代码上有一个空间上的小优化:在建残余网络时,我们还需要原网络中的每一条边再建一个正边和反边。也就是说题目中无向图的每一条边对应到我们的残余网络中,都有
额。。。也许我说的不是很清楚,具体看代码吧。。。
AC Code
// 《 肥 肠 煎 蛋 》
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <sstream>
#include <cstdio>
#include <queue>
using namespace std;
const int N = 205, M = 80005;
int h[N], e[M], ne[M], w[M], f[M], idx;
int n, m, S, T, t;
int d[N], cur[N];
bool vis[N];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
e[idx] = a, ne[idx] = h[b], w[idx] = c, h[b] = idx++;
}
bool bfs() {
memset(vis, 0, sizeof(vis));
memset(d, -1, sizeof(d));
queue<int> q;
q.push(S);
d[S] = 0;
vis[S] = 1;
cur[S] = h[S];
while (q.size()) {
int t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (!vis[j] && f[i]) {
vis[j] = 1;
d[j] = d[t] + 1;
cur[j] = h[j];
if (j == T) return 1;
q.push(j);
}
}
}
return 0;
}
int dfs(int u, int limit) {
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; (~i) && flow < limit; i = ne[i]) {
int j = e[i];
cur[u] = i;
if (d[j] == d[u] + 1 && f[i]) {
int k = dfs(j, min(f[i], limit - flow));
if (!k) d[j] = -1;
f[i] -= k;
f[i ^ 1] += k;
flow += k;
}
}
return flow;
}
int dinic() {
int r = 0, flow;
while (bfs()) {
while (flow = dfs(S, 1e9)) {
r += flow;
}
}
return r;
}
bool check(int x) {
for (int i = 0; i < idx; i++) {
if (w[i] <= x) f[i] = 1;
else f[i] = 0;
}
return dinic() >= t;
}
int main() {
memset(h, -1, sizeof(h));
cin >> n >> m >> t;
S = 1, T = n;
for (int i = 1; i <= m; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
int l = 1, r = 1e6;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l << endl;
return 0;
}