新网络流不止24题
P2756 飞行员配对问题
二分图最大匹配,没啥好讲的。code
P1251 餐巾计划问题
自然地想到把餐巾当成流量。将每个点和汇点连边,容量为 \(r_i\),这样跑最大流就能保证每个点恰好跑 \(r_i\) 个餐巾;将每个点和源点连边,容量 \(r_i\)。可这样黑心商家就能用脏餐巾了,于是考虑将点 \(u\) 拆成 \(u_{dir}\) 与 \(u_{clr}\),\(s\) 连 \(u_{dir}\) 表示每天会从源点进来 \(r_i\) 个脏餐巾,\(t\) 连 \(u_{clr}\) 表示每天会对汇点汇进去 \(r_i\) 个干净的餐巾。然后将 \(s\) 与 \(u_{clr}\) 连一条容量 \(\inf\) 费用 \(p\) 的边表示买入干净毛巾,\(u_{dir}\) 与 \((u+1)_{dir}\) 连边表示脏毛巾留在第二天,\(u_{dir}\) 连在快洗、慢洗之后的点的 \(clr\) 分点上,费用就是钱,容量 \(\inf\),然后跑最小费用最大流。code
P2764 最小路径覆盖问题
最小路径覆盖集问题,见基础知识博客。code
P1646 happinesss
我们知道这些条件肯定要割掉其中一个,于是统计答案的方式我们改成总数减去割,于是问题转化为最小割问题。考虑将一个点拆成文科和理科,然后源点连文科,理科连汇点,对于每组相邻点,我们建一个新点,让这个新点连代表这两个点,然后这个点根据文理连源汇,这两个点和新点连边,跑最大流就行了。code
P4003 无限之环
神仙题啊啊啊!!!!!!
套路化地,我们可以建超级源汇,黑白染色。然后我们发现不会做。于是我们发现了 qyc 的 PPT
qyc 的 PPT 里写了这么一句话:
匹配!匹配!
这启发我们什么?
我们考虑两个接口对接等价于让两个水管旋到同一个点上。
这又启发我们什么?
拆点!把每个方格拆成他自己、左端点、右端点、上端点、下端点五个点。
然后注意到这些管子里流的是水。这启发我们什么?
网络流!!!
然后旋转有代价。这启发我们什么?
费用流!!!
然后大胆建模,没有漏水等价于满流。然后就过了!!!
为什么有的题解写了 300 多行?压压行可以写 100 来行啊。
上!代!码!
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 5, INF = 1e9;
const int dx[] = {0, -1, 0, 1, 0};
const int dy[] = {0, 0, 1, 0, -1};
const int px[] = {0, 3, 4, 1, 2};
// a b c d 数的二进制表示
// 8 4 2 1 位权
// 左 下 右 上 方向
// 4 3 2 1 编号
int n, m, a[N], head[N], tot = 1, S, T, sum;
struct edge
{
int v, w, c, nxt;
} e[N];
inline void add(int u, int v, int w, int c)
{
e[++tot].v = v, e[tot].w = w, e[tot].c = c;
e[tot].nxt = head[u], head[u] = tot;
}
inline void addedge(int u, int v, int w, int c) { add(u, v, w, c), add(v, u, -w, 0); }
int cur[N], dis[N], mincost, maxflow;
bool vis[N];
queue<int> q;
inline bool spfa()
{
for (int i = 0; i <= T; i++) dis[i] = INF, vis[i] = false;
dis[S] = 0, vis[S] = true;
q.push(S);
while (!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].v;
if (dis[v] > dis[u] + e[i].w && e[i].c)
{
dis[v] = dis[u] + e[i].w;
if (!vis[v]) q.push(v), vis[v] = true;
}
}
}
return dis[T] != INF;
}
int dfs(int u, int minf)
{
if (u == T || !minf) return minf;
int flow = 0, f;
vis[u] = true;
for (int i = cur[u]; i; i = e[i].nxt)
{
int v = e[i].v;
cur[u] = i;
if (!vis[v] && e[i].c && dis[v] == dis[u] + e[i].w)
{
f = dfs(v, min(minf, e[i].c));
if (f) minf -= f, flow += f, e[i].c -= f, e[i ^ 1].c += f, mincost += f * e[i].w;
if (!minf) return vis[u] = false, flow;
}
}
return vis[u] = false, flow;
}
inline void MCMF()
{
while (spfa())
{
for (int i = 1; i <= T; i++) cur[i] = head[i];
while (int x = dfs(S, INF)) maxflow += x;
}
}
inline int num(int i, int j, int op) { return (i - 1) * m + j + op * n * m; }
inline void work(int x, int y, int val)
{
if ((x + y) & 1)
{
addedge(S, num(x, y, 0), 0, INF);
for (int i = 1; i <= 4; i++)
{
int idx = x + dx[i], idy = y + dy[i];
if (idx < 1 || idx > n || idy < 1 || idy > m) continue;
addedge(num(x, y, i), num(idx, idy, px[i]), 0, 1);
}
for (int i = 1; i <= 4; i++)
if ((val & (1 << (i - 1)))) addedge(num(x, y, 0), num(x, y, i), 0, 1), ++sum;
int up = num(x, y, 1), rt = num(x, y, 2), dn = num(x, y, 3), lt = num(x, y, 4);
switch (val)
{
case 1: addedge(up, rt, 1, 1), addedge(up, dn, 2, 1), addedge(up, lt, 1, 1); break;
case 2: addedge(rt, up, 1, 1), addedge(rt, dn, 1, 1), addedge(rt, lt, 2, 1); break;
case 4: addedge(dn, up, 2, 1), addedge(dn, lt, 1, 1), addedge(dn, rt, 1, 1); break;
case 8: addedge(lt, up, 1, 1), addedge(lt, rt, 2, 1), addedge(lt, dn, 1, 1); break;
case 3: addedge(rt, lt, 1, 1), addedge(up, dn, 1, 1); break;
case 6: addedge(dn, up, 1, 1), addedge(rt, lt, 1, 1); break;
case 9: addedge(lt, rt, 1, 1), addedge(up, dn, 1, 1); break;
case 12: addedge(lt, rt, 1, 1), addedge(dn, up, 1, 1); break;
case 7: addedge(up, lt, 1, 1), addedge(rt, lt, 2, 1), addedge(dn, lt, 1, 1); break;
case 11: addedge(up, dn, 2, 1), addedge(rt, dn, 1, 1), addedge(lt, dn, 1, 1); break;
case 13: addedge(up, rt, 1, 1), addedge(dn, rt, 1, 1), addedge(lt, rt, 2, 1); break;
case 14: addedge(rt, up, 1, 1), addedge(lt, up, 1, 1), addedge(dn, up, 2, 1); break;
}
}
else
{
addedge(num(x, y, 0), T, 0, INF);
for (int i = 1; i <= 4; i++)
if ((val & (1 << (i - 1)))) addedge(num(x, y, i), num(x, y, 0), 0, 1), ++sum;
int up = num(x, y, 1), rt = num(x, y, 2), dn = num(x, y, 3), lt = num(x, y, 4);
switch (val)
{
case 1: addedge(rt, up, 1, 1), addedge(dn, up, 2, 1), addedge(lt, up, 1, 1); break;
case 2: addedge(up, rt, 1, 1), addedge(lt, rt, 2, 1), addedge(dn, rt, 1, 1); break;
case 4: addedge(up, dn, 2, 1), addedge(lt, dn, 1, 1), addedge(rt, dn, 1, 1); break;
case 8: addedge(up, lt, 1, 1), addedge(dn, lt, 1, 1), addedge(rt, lt, 2, 1); break;
case 3: addedge(lt, rt, 1, 1), addedge(dn, up, 1, 1); break;
case 6: addedge(up, dn, 1, 1), addedge(lt, rt, 1, 1); break;
case 9: addedge(rt, lt, 1, 1), addedge(dn, up, 1, 1); break;
case 12: addedge(rt, lt, 1, 1), addedge(up, dn, 1, 1); break;
case 7: addedge(lt, rt, 2, 1), addedge(lt, up, 1, 1), addedge(lt, dn, 1, 1); break;
case 11: addedge(dn, up, 2, 1), addedge(dn, lt, 1, 1), addedge(dn, rt, 1, 1); break;
case 13: addedge(rt, up, 1, 1), addedge(rt, dn, 1, 1), addedge(rt, lt, 2, 1); break;
case 14: addedge(up, rt, 1, 1), addedge(up, lt, 1, 1), addedge(up, dn, 2, 1); break;
}
}
}
int main()
{
cin.tie(0)->sync_with_stdio(false);
cout.tie(0);
cin >> n >> m;
S = n * m * 5 + 1, T = S + 1;
for (int i = 1, val; i <= n; i++)
for (int j = 1; j <= m; j++) cin >> val, work(i, j, val);
MCMF();
if (maxflow * 2 != sum) cout << -1;
else cout << mincost;
return 0;
}
也不长啊。