洛谷-B3608 题解

题目传送门

一道网络流题……

费用流板子题。费用流实际上是在给最大流套个最短路,而费用流一般边权会有负数,所以用 SPFA 算法,关于 SPFA,它复活了

可以在最大流做 bfs 的时候将 SPFA 套上去。数据似乎比较凶残,建议使用 Dinic 来求最大流。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <bits/stdc++.h>

namespace xvl_ {
#define ll long long
#define IMAX 1e9
#define LMAX 1e18
void debug() { std :: cerr << "debug" << "\n"; }
template <typename T> inline T Max(T a, T b) { return a > b ? a : b; }
template <typename T> inline T Min(T a, T b) { return a < b ? a : b; }
template <typename T, typename... Args> inline T Max(T a, Args... args) { return a > Max(args...) ? a : Max(args...); }
template <typename T, typename... Args> inline T Min(T a, Args... args) { return a < Min(args...) ? a : Min(args...); }
}
using namespace std;
using namespace xvl_;
struct Edge { ll to, r, c, cp; };
ll n, m, s, t, ans1, ans2;
ll dis[405], vis[405];
bool inq[405], isz[405];
vector <Edge> G[405];
void Add_Edge(ll u, ll v, ll c, ll w) {
G[u].push_back({v, c, w, (int)(G[v].size())});
G[v].push_back({u, 0, -w, (int)(G[u].size() - 1)});
}
bool check(ll s, ll t) {
queue <ll> q;
fill(dis + 1, dis + 1 + 400, LMAX);
dis[s] = 0, inq[s] = 1, q.push(s);
while (!q.empty()) {
ll cur = q.front();
q.pop();
inq[cur] = 0;
for (auto v : G[cur]) {
if (dis[cur] + v.c < dis[v.to] and v.r) {
dis[v.to] = dis[cur] + v.c;
if (!inq[v.to]) inq[v.to] = 1, q.push(v.to);
}
}
}
return dis[t] != LMAX;
}
ll aug(ll cur, ll now, ll& c) {
ll flow = 0;
if (cur == t) return now;
isz[cur] = 1;
for (ll& i = vis[cur]; i < G[cur].size(); i++) {
Edge& v = G[cur][i];
if (dis[v.to] != dis[cur] + v.c or isz[v.to] or !v.r) continue;
int d = aug(v.to, Min(now - flow, v.r), c);
v.r -= d, G[v.to][v.cp].r += d, flow += d, c += v.c * d;
if (flow == now) break;
}
isz[cur] = 0;
return flow;
}
int main() {
/*
freopen("InName.in", "r", stdin);
freopen("OutName.out", "w", stdout);
*/
ios :: sync_with_stdio(0);
cin >> n >> m;
s = 1, t = n;
for (int i = 1; i <= m; i++) {
ll u, v, c, w;
cin >> u >> v >> c >> w;
Add_Edge(u, v, c, w);
}
while (check(s, t)) {
memset(vis, 0, sizeof vis);
ans1 += aug(s, LMAX, ans2);
}
cout << ans1 << " " << ans2;
return 0;
}