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;
}

希望能帮助到大家!

posted @ 2023-01-03 16:48  liangbowen  阅读(41)  评论(0编辑  收藏  举报