P3701 题解
前言
比较简单的最大流基础建图题。
如果认为思路部分过于复杂,可以直接看图。
思路
以下为了方便,我们把 byx 称作 A,把诗乃酱称作 B。
首先发现,人物的唯一限制就是寿命。我们直接以此来建网络即可。
注意到有一个特殊的限制:
当 J 的寿命为 \(0\) 时,同一棵树上的 YYY 可以为他延长一秒的寿命。
这说明,YYY 的寿命可能不是他的初始寿命。我们可以记录一下双方各有几个 J。
假设 A 有 \(cnt\) 个 J,那么 YYY 的寿命就是 \(w + cnt\)。B 方同理。
那么我们现在得到了所有人真实的寿命,所以:
- 从源点向每个 A 的人连边,流量是人物的寿命。
- 从每个 B 的人向汇点连边,流量是人物的寿命。
人物与人物之间有克制关系,我们以此为依据建立 A 与 B 人物之间的网络。
这个就简单了:如果 \(a_i\) 克制 \(b_j\),建立 \(i \to j\) 的流量为 \(1\) 的边即可,此处的 \(1\) 的意义是,获胜的比赛数量。
然后再上一个 Dinic 板子即可。
注意,最后的答案要与 \(m\) 取一个 \(\min\)!
建图
有一说一,看一眼这个图就懂了啊。
代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int N = 205;
struct Edge {int now, nxt, w;} e[N * 2 + N * N];
int head[N], _head[N], cur = 1;
void add(int u, int v, int w)
{
e[++cur].now = v, e[cur].nxt = head[u], e[cur].w = w;
head[u] = cur;
}
void ADD(int u, int v, int w) {add(u, v, w), add(v, u, 0);}
int s, t;
bool vis[N]; int dis[N];
bool bfs()
{
queue <int> q;
memset(vis, false, sizeof vis);
q.push(s), vis[s] = true, dis[s] = 0, _head[s] = head[s];
while (!q.empty())
{
int u = q.front(); q.pop();
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].now;
if (vis[v] || !e[i].w) continue;
vis[v] = true, dis[v] = dis[u] + 1, _head[v] = head[v];
if (v == t) return true;
q.push(v);
}
}
return false;
}
int dfs(int u, int maxflow)
{
if (u == t) return maxflow;
int flow = 0;
for (int i = _head[u]; i && flow < maxflow; i = e[i].nxt)
{
_head[u] = i;
int v = e[i].now;
if (dis[v] != dis[u] + 1 || !e[i].w) continue;
int ww = dfs(v, min(maxflow - flow, e[i].w));
if (ww == 0) dis[v] = -114514;
e[i].w -= ww, e[i ^ 1].w += ww, flow += ww;
}
return flow;
}
int dinic()
{
int ans = 0, flow;
while (bfs())
while (flow = dfs(s, 2147483647))
ans += flow;
return ans;
}
const bool dict[5][5] = { //克制关系
0, 1, 1, 0, 0,
0, 0, 1, 1, 0,
0, 0, 0, 1, 1,
1, 0, 0, 0, 1,
1, 1, 0, 0, 0
};
int read() //为了方便把人物转化为数字
{
string s;
cin >> s;
if (s == "J") return 0;
if (s == "HK") return 1;
if (s == "W") return 2;
if (s == "E") return 3;
if (s == "YYY") return 4;
}
int a[N], b[N];
int main()
{
ios::sync_with_stdio(false);
int n, m, cnta = 0, cntb = 0;
cin >> n >> m;
s = 0, t = 2 * n + 1;
for (int i = 1; i <= n; i++)
{
a[i] = read();
if (a[i] == 4) cnta++; //统计YYY的个数
}
for (int i = 1; i <= n; i++)
{
b[i] = read();
if (b[i] == 4) cntb++; //统计YYY的个数
}
for (int i = 1; i <= n; i++)
{
int w;
cin >> w;
if (a[i] == 0) w += cnta; //每一个YYY都可以给J续命
ADD(s, i, w);
}
for (int i = 1; i <= n; i++)
{
int w;
cin >> w;
if (b[i] == 0) w += cntb; //每一个YYY都可以给J续命
ADD(i + n, t, w);
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (dict[a[i]][b[j]]) //根据克制关系建边
ADD(i, j + n, 1);
cout << min(dinic(), m);
return 0;
}
希望能帮助到大家!