牛客练习赛40 F-小D的剑阵 最小割+二元关系建图

牛客练习赛40 F-小D的剑阵 最小割+二元关系建图


传送门: https://ac.nowcoder.com/acm/contest/369/F

题意

在这里插入图片描述

思路

数 据 范 围 不 大 , 而 且 存 在 二 元 限 制 关 系 , 考 虑 网 络 流 。 数据范围不大,而且存在二元限制关系,考虑网络流。
在这里插入图片描述

假 如 x 和 y 存 在 二 元 限 制 关 系 , 所 以 考 虑 x 和 y 是 否 选 取 。 假如x和y存在二元限制关系,所以考虑x和y是否选取。 xyxy

如 果 考 虑 选 取 呢 ? 最 小 割 ! 设 与 源 点 s 在 一 部 分 的 点 不 取 , 与 汇 点 在 一 部 分 的 点 取 。 如果考虑选取呢?最小割!设与源点s在一部分的点不取,与汇点在一部分的点取。 s

将 所 有 的 w 和 v 0 和 v 1 加 起 来 , 然 后 把 最 小 割 认 为 是 代 价 , 答 案 就 是 s u m − 最 小 割 ( 最 小 代 价 ) 。 将所有的w和v_0和v_1加起来,然后把最小割认为是代价,答案就是sum-最小割(最小代价)。 wv0v1sum()

设 w x , w y , v 0 , v 1 , v 2 表 示 x 的 权 值 , y 的 权 值 , x 和 y 都 选 的 权 值 , x 和 y 都 不 选 的 权 值 , x 和 y 只 选 一 个 的 权 值 。 设w_x,w_y,v_0,v_1,v_2表示x的权值,y的权值,x和y都选的权值,x和y都不选的权值,x和y只选一个的权值。 wx,wy,v0,v1,v2xyxyxyxy

为 什 么 没 有 v 2 ? 因 为 v 2 是 扣 除 的 , 不 是 代 价 , v 2 要 在 我 们 最 小 割 模 型 中 会 被 算 进 去 , 最 后 会 减 掉 。 为什么没有v_2?因为v_2是扣除的,不是代价,v_2要在我们最小割模型中会被算进去,最后会减掉。 v2v2v2

则 对 于 上 面 的 模 型 , 最 小 割 有 四 种 : 则对于上面的模型,最小割有四种:

  • x 和 y 都 选 , 最 小 割 为 a + c = v 1 x和y都选,最小割为a+c=v_1 xya+c=v1
  • x 和 y 都 不 选 , 最 小 割 为 b + d = w x + w y + v 0 x和y都不选,最小割为b+d=w_x+w_y+v_0 xyb+d=wx+wy+v0
  • x 选 , y 不 选 , 最 小 割 为 a + f + d = w y + v 0 + v 1 + v 2 x选,y不选,最小割为a+f+d=w_y+v_0+v_1+v_2 xya+f+d=wy+v0+v1+v2
  • x 不 选 , y 选 , 最 小 割 为 c + e + b = w x + v 0 + v 1 + v 2 x不选,y选,最小割为c+e+b=w_x+v_0+v_1+v_2 xyc+e+b=wx+v0+v1+v2

然 后 就 要 构 造 边 的 容 量 了 。 然后就要构造边的容量了。

b 和 d 分 别 是 连 向 汇 点 的 边 , 则 为 w x + v 0 2 和 w y + v 0 2 。 b和d分别是连向汇点的边,则为w_x+\frac{v_0}{2}和w_y+\frac{v_0}{2}。 bdwx+2v0wy+2v0
a 和 c 分 别 是 连 向 源 点 的 边 , 则 为 v 1 2 和 v 1 2 a和c分别是连向源点的边,则为\frac{v_1}{2}和\frac{v_1}{2} ac2v12v1
剩 下 的 e 和 f 解 出 来 为 v 2 + v 0 + v 1 2 剩下的e和f解出来为v_2+\frac{v_0+v_1}{2} efv2+2v0+v1

Code

#include "bits/stdc++.h"
using namespace std;

struct Dinic {
    static const int N = 1e3 + 10, M = 1e5 + 10, INF 0x3f3f3f3f;
    int n, m, s, t;
    int maxflow;
    int deep[N], cur[N];

    struct Edge {
        int v, next;
        int cap;
    }e[M << 1];
    int head[M << 1], cnt;

    void init() {
        memset(head, -1, sizeof head);
        cnt = maxflow = 0;
    }

    void add(int u, int v, int cap) {
        e[cnt].v = v;
        e[cnt].cap = cap;
        e[cnt].next = head[u];
        head[u] = cnt++;

        e[cnt].v = u;
        e[cnt].cap = 0;
        e[cnt].next = head[v];
        head[v] = cnt++;
    }

    bool bfs() {
        for(int i = 0;i <= t; i++) {
            deep[i] = -1, cur[i] = head[i];
        }
        queue<int> q;
        q.push(s); deep[s] = 0;
        while(!q.empty()) {
            int u = q.front(); q.pop();
            for(int i = head[u]; ~i; i = e[i].next) {
                int v = e[i].v;
                if(deep[v] == -1 && e[i].cap) {
                    deep[v] = deep[u] + 1;
                    q.push(v);
                }
            }
        }
        if(deep[t] >= 0) return true;
        else return false;
    }

    int dfs(int u, int mx) {
        int a;
        if(u == t) return mx;
        for(int i = cur[u]; ~i; i = e[i].next) {
            cur[u] = i;
            int v = e[i].v;
            if(e[i].cap && deep[v] == deep[u] + 1 && (a = dfs(v, min(mx, e[i].cap)))) {
                e[i].cap -= a;
                e[i ^ 1].cap += a;
                return a;
            }
        }
        return 0;
    }

    void dinic() {
        while(bfs()) {
            while(1) {
                int res = dfs(s, INF);
                if(!res) break;
                maxflow += res;
            }
        }
    }
}mf;

void solve() {
    int n, q; cin >> n >> q;
    mf.init();
    int ans = 0;
    mf.n = n; mf.s = 0; mf.t = n + 1;
    vector<int> w(n + 1), m(n + 1);
    for(int i = 1;i <= n; i++) cin >> w[i], ans += w[i];
    for(int i = 1;i <= q; i++) {
        int x, y, v0, v1, v2; cin >> x >> y >> v0 >> v1 >> v2;
        ans += v0 + v1;
        w[x] += v0 / 2; w[y] += v0 / 2;
        m[x] += v1 / 2; m[y] += v1 / 2;
        mf.add(x, y, v2 + (v0 + v1) / 2);
        mf.add(y, x, v2 + (v0 + v1) / 2);
    }
    for(int i = 1;i <= n; i++) {
        mf.add(mf.s, i, m[i]);
        mf.add(i, mf.t, w[i]);
    }
    mf.dinic();
    cout << ans - mf.maxflow << endl;
}

signed main() {
    solve();
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值