[BZOJ3876]支线剧情

5 篇文章 0 订阅

题目链接BZOJ3876

题目大意
有一个有向无环联通图,每条边有费用,1号点入度为0。每趟可以从1号点开始,可以走到任意一点结束,要求每条边走一遍,可以走多趟,问最小化费用。

分析
上下界费用流,相关知识在上下界网络流
1. 新建源汇S,T。
2. 对于每条边 ai bi ci :S向 bi 连边,流量为1,费用为 ci ai 向T连边,流量为1,费用为0; ai bi 连边,流量为INF,费用为 ci
3. 对于每个非1号点,向1号点连边,流量为INF,费用为0。然后跑一遍费用流。

上代码

// 极限时限,大常数反面典型
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 3e2 + 10;
const int M = 2e4 + 10;
const int INF = 0x3f3f3f3f;

int n;
inline int read() {
    register char ch = getchar();
    register int ans = 0, neg = 1;
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-') neg = -1;
    for (; isdigit(ch); ch = getchar())
        ans = ans * 10 + ch - '0';
    return ans * neg;
}

int S, T;
int cnt, id[N];

int head[N], len;
struct Lib {
    int to, nxt, flow, cost;
    inline void add(int a, int b, int c, int d) {
        to = b, flow = c, cost = d;
        nxt = head[a], head[a] = len++;
    }
} lib[M << 1];
inline void makePath(int a, int b, int c, int d) {
    lib[len].add(a, b, c, d), lib[len].add(b, a, 0, -d);
}

inline void init() {
    n = read(), len = 2;
    S = ++cnt, T = ++cnt;
    for (int i = 1; i <= n; ++i) id[i] = ++cnt;
    for (int i = 1; i <= n; ++i) {
        if (i != 1) makePath(id[i], id[1], INF, 0);
        for (int j = 1, k = read(); j <= k; ++j) {
            int a = read(), b = read();
            makePath(S, id[a], 1, b);
            makePath(id[i], T, 1, 0);
            makePath(id[i], id[a], INF, b);
        }
    }
}

queue <int> Q;
bool inQ[N];
int dist[N], preV[N], preE[N];
bool SPFA() {
    memset(dist, 0x3f, sizeof(dist));
    dist[S] = 0, Q.push(S), inQ[S] = true;
    while (!Q.empty()) {
        int tmp = Q.front();
        Q.pop(), inQ[tmp] = false;
        for (int p = head[tmp]; p; p = lib[p].nxt) {
            int now = lib[p].to, cost = lib[p].cost;
            if (lib[p].flow && dist[now] > dist[tmp] + cost) {
                dist[now] = dist[tmp] + cost;
                preE[now] = p, preV[now] = tmp;
                if (!inQ[now]) Q.push(now), inQ[now] = true;
            }
        }
    }
    return dist[T] < INF;
}
int MCMF() {
    int ans = 0;
    while (SPFA()) {
        int maxf = INF;
        for (int i = T; i != S; i = preV[i])
            maxf = min(maxf, lib[preE[i]].flow);
        for (int i = T; i != S; i = preV[i])
            lib[preE[i]].flow -= maxf, lib[preE[i] ^ 1].flow += maxf;
        ans += dist[T] * maxf;
    }
    return ans;
}

int main() {
    init();
    printf("%d\n", MCMF());
    return 0;
}

以上

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值