「Fortuna OJ」4682「GDOI2017模拟8.11」生物学家

主要思路

这道题原题解作者的思路非常的清晰。我来阐述一下。

首先思考答案的意义,一定是总的权值和减去:

  • 变性花费
  • 不要的赞助费
  • 喝茶费用

我们可以用上面这三个元素组一个网络流,计算最小割使答案最大。

考虑将源点连入雌性,雄性连入雄性,流上限就是变性花费:如果将这种边割掉,那么就是不需要进行变性。考虑朋友的边,如果倾向于变雌性,源点连入,向所有的对应编号连边;如果雄性,连入汇点,所有对应编号的向该朋友连边。

代码

// FOJ4682.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e5 + 200, INF = 0x3f3f3f3f;

int head[MAX_N], current, st, ed, dep[MAX_N], cur[MAX_N];
int n, m, g, sex[MAX_N];

struct edge
{
    int to, nxt, weight;
} edges[MAX_N << 2];

void addpath(int src, int dst, int weight)
{
    edges[current].to = dst, edges[current].nxt = head[src];
    edges[current].weight = weight, head[src] = current++;
}

void addtube(int src, int dst, int weight)
{
    addpath(src, dst, weight);
    addpath(dst, src, 0);
}

bool bfs()
{
    memset(dep, 0, sizeof(dep));
    queue<int> q;
    q.push(st), dep[st] = 1;
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = edges[i].nxt)
            if (edges[i].weight > 0 && dep[edges[i].to] == 0)
                q.push(edges[i].to), dep[edges[i].to] = dep[u] + 1;
    }
    return dep[ed] != 0;
}

int dfs(int u, int flow)
{
    if (u == ed || flow == 0)
        return flow;
    for (int &i = cur[u]; i != -1; i = edges[i].nxt)
        if (edges[i].weight > 0 && dep[edges[i].to] == dep[u] + 1)
            if (int di = dfs(edges[i].to, min(flow, edges[i].weight)))
            {
                edges[i].weight -= di, edges[i ^ 1].weight += di;
                return di;
            }
    return 0;
}

int dinic()
{
    int ans = 0;
    while (bfs())
    {
        memcpy(cur, head, sizeof(head));
        while (int di = dfs(st, INF))
            ans += di;
    }
    return ans;
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d%d%d", &n, &m, &g);
    st = n + m + 1, ed = st + 1;
    for (int i = 1; i <= n; i++)
        scanf("%d", &sex[i]);
    for (int i = 1, ci; i <= n; i++)
    {
        scanf("%d", &ci);
        if (sex[i] == 0)
            addtube(st, i, ci);
        else
            addtube(i, ed, ci);
    }
    int answer = 0;
    for (int i = 1, wi, certainSex, ki, id, isFriend; i <= m; i++)
    {
        scanf("%d%d%d", &certainSex, &wi, &ki);
        answer += wi;
        while (ki--)
        {
            scanf("%d", &id);
            if (certainSex == 0)
                addtube(i + n, id, INF);
            else
                addtube(id, i + n, INF);
        }
        scanf("%d", &isFriend);
        if (certainSex == 0)
            addtube(st, i + n, wi + ((isFriend) ? g : 0));
        else
            addtube(i + n, ed, wi + ((isFriend) ? g : 0));
    }
    answer -= dinic();
    printf("%d", answer);
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *