P2974 网络流
Usada_Pekora · · 题解
题意:
给定
每个点上有 T 牛,J 牛,或者没有(E)。
J 牛可以 MOVE 到一个相邻的点,也可以 ATTACK 相邻点上的一个 T 牛。不过操作有限制,只能按照 MOVE,ATTACK 或者 MOVE 然后 ATTACK 三种方式操作。
一个 T 牛仅能被 ATTACK 一次。
需要保证任意时刻,每个点上有且仅有一头牛。
求所有 T 牛被 ATTACK 的最大次数,并给出一个可行的操作方案。
题解:
注意到这个东西很像最大流的常见套路:每个点有容量限制,多个源点,问多个汇点最多接收到多少流量?
按点种类建模。
对于 J 牛,它需要从超级源接收一个流量(攻击一次),然后可以 MOVE 到一个其他点或者不动,最后 ATTACK,需要三层图。
对于 T 牛,它只能被一个点 ATTACK 一次,需要一层图。
对于 E,它可以被 MOVE,但是同时只能有一头牛在这个位置,所以需要两层用来限制流量。
梳理一下:第一层给 J 牛接收流量,第二层是被 MOVE 到或者不动的点,第三层用于限制每个点至多被一头牛占据,第四层被 ATTACK。
然后就可以愉快 Dinic 了。
至于方案输出,我们像匈牙利算法一样去处理 MOVE 并且模拟就好了,不同的一点是每个点最多 MOVE 一次。ATTACK 在处理完前面以后就是常规的最大流输出了。
Dinic 可以过倒不是因为网络流跑不满,而是因为流量都是
#include <bits/stdc++.h>
using namespace std;
const int N = 1005, M = 5005;
int fir[N << 2], nxt[(N + M) << 2], to[(N + M) << 2], flow[(N + M) << 2], cnt = 1;
int n, m, s, t, dep[N << 2];
char str[N];
bool vis[N << 2];
inline void add(int u, int v, int f) {
to[++cnt] = v;
flow[cnt] = f;
nxt[cnt] = fir[u];
fir[u] = cnt;
}
inline void addedge(int u, int v, int f) {
add(u, v, f);
add(v, u, 0);
}
#define v to[i]
inline bool bfs() {
memset(dep, 0, sizeof dep);
dep[s] = 1;
queue<int> q;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = fir[u]; i; i = nxt[i]) {
if (flow[i] && !dep[v]) {
q.push(v);
dep[v] = dep[u] + 1;
}
}
}
return dep[t] > 0;
}
inline int min(int x, int y) {
return x < y ? x : y;
}
inline int dfs(int u, int in) {
if (u == t)
return in;
int out = 0, res;
for (int i = fir[u]; i && in; i = nxt[i]) {
if (dep[v] == dep[u] + 1 && flow[i]) {
res = dfs(v, min(in, flow[i]));
flow[i] -= res, flow[i ^ 1] += res, in -= res, out += res;
}
}
if (out == 0)
dep[u] = 0;
return out;
}
inline int dinic() {
int res = 0;
while (bfs())
res += dfs(s, 1e9);
return res;
}
inline bool print_move(int u) {
if (vis[u] || str[u] != 'J')
return 0;
vis[u] = 1;
for (int i = fir[u]; i; i = nxt[i]) {
if (v == u + n || v <= u || flow[i])
continue;
if (str[v - n] == 'J') {
if (print_move(v - n)) {
printf("MOVE %d %d\n", u, v - n);
swap(str[u], str[v - n]);
return 1;
}
} else if (str[v - n] == 'E') {
printf("MOVE %d %d\n", u, v - n);
swap(str[u], str[v - n]);
return 1;
}
}
return 0;
}
#undef v
signed main() {
scanf("%d%d", &n, &m);
scanf("%s", str + 1);
s = 0, t = 4 * n + 1;
for (int i = 1; i <= n; i++) {
if (str[i] == 'J') {
addedge(s, i, 1);
addedge(i, n + i, 1);//REMAIN
addedge(n + i, 2 * n + i, 1);
} else if (str[i] == 'T')
addedge(3 * n + i, t, 1);
else if (str[i] == 'E')
addedge(n + i, 2 * n + i, 1);
}
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
if (str[u] != 'T' && str[v] != 'T') { //MOVE u v / MOVE v u
addedge(u, n + v, 1);
addedge(v, n + u, 1);
} else if (str[u] != 'T' && str[v] == 'T') // ATTACK u v
addedge(2 * n + u, 3 * n + v, 1);
else if (str[v] != 'T' && str[u] == 'T') //ATTACK v u
addedge(2 * n + v, 3 * n + u, 1);
}
int maxf = dinic();
printf("%d\n", maxf);
for (int i = 1; i <= n; i++)
print_move(i);
for (int i = 2 * n + 1; i <= 3 * n; i++) {
if (str[i - 2 * n] != 'J')
continue;
for (int j = fir[i]; j; j = nxt[j]) {
int v = to[j];
if (v <= i)
continue;
if (flow[j] == 0 && str[v - 3 * n] == 'T') {
printf("ATTACK %d %d\n", i - 2 * n, v - 3 * n);
break;
}
}
}
return 0;
}