有上下限的最小费用可行流

例题 BZOJ 3876


  题意,有N个点,并且有一些边,每个点必须要经过一次,但是不限经过次数,也就是有下界的情况,且下界为1,现在每次的起点都是1,问至少花费的代价,使得每次从1开始,终点不限的情况下,走完所有的边(这里所谓的支线指的是每条边,点都被称之为剧情点)。

  于是,很好发现是在说明一个有下限、无上限的最小费用可行流的这么一个问题了。

最小费用可行流的模型

给定一个网络,一个加权的有向图 G ,其中的每条边都有一个容量上界 C 。其中的两点: S 只有出度没有入度, T 只有入度没有出度。求 S 到 T 最大可以流过的流量,这是最大流的模型。
且满足以下条件:

  • 容量限制:每条边的流量\large 0 <= f <= C
  • 流量平衡:任意一个点i,流入等于流出

那么,f是G的一个可行流,最大流即满足容量限制和流量平衡的最大的流。

如果在网络中,每条边增加一个流量下界B,这就是有上下界限制的网络流的模型了。

那么有上下界限制的网络流也是满足两个条件:

  • 容量限制:每条边的流量\large B <= f <= C
  • 流量平衡:任意一个点i,流入等于流出

今天,我们要解决的这道题,就是一个有源汇有下界的最小费用可行流的这样一个问题。(其中比较特殊的是任意非1号结点都可以当作是汇点)

但是,首先,我们要先讲讲无源汇有上下限的最大流问题

顾名思义,无源汇上下界可行流:没有源点 S ,汇点 T 。在网络中求可行流或者指出不存在。

对于这个问题,不好处理,但是如果我们去掉流量下界限制 B ,那么就是最大流的模型了,问题就可以解决了。

但是,我们不能直接去掉,因为有可能存在入!=出的情况。也就是说,当我们如果直接减去的话,会使得网络流的流量不守恒了。

那么,我们则需要加上附加流使得它达到一个平衡了,加多少呢?也就是每个点的出入流之间的差距取正来决定的。假设附加流是g,那么最后的实际流量就是B+g了。

此时我们去掉了流量下界限制 B ,那么网络中每条边的容量上界限也要减去,已经流过了 B 的流量,即新网络图中每条边的流量上界限制为 C - B ,下界限制0。

于是问题便成了求解最大流的问题了。

那么再看到有源汇的有上下限的最大流问题。

有源汇上下界可行流相比有源汇上下界可行流,多了源点 S 和汇点 T ,求从 S 到 T 满足每条边的流量都满足限制,且除S、T ,其他点都满足流量平衡。因为只有 S 和 T 不满足流量平衡,所以,如果可以使 S、T 也满足流量平衡,那么就可以直接套用无源汇上下界可行流了。

这里,我们定义超级源点\large Super _ S和超级汇点\large Super _ T

源点的性质是只有流出的没有流入的,汇点恰好相反,而且对于源点流出的和汇点流入的,这些流量是相等的。所以建一条从 T 到 S 的边容量为INF,那么流入汇点T的流量就会从这条边流入 S 。有源汇到无源汇转换完成,跑一遍从 \large Super _ S 到 \large Super _ T 的最大流即可。可行流的流量也就是这条边的流量。

  回到之前的问题上去。

  我们现在要求解的是有源汇的有下限的最小费用可行流,有了上述的知识点,我们构造边,再跑一个最小费用流即可。

  那么,每个点都是要经过至少一次,对于所有的出入流还要做到流量守恒,所以根据下限,我们可以确定网络流的最少流入,根据最少流入,我们就知道每个点的流入减去流出的值的情况,于是,流入多于流出的,我们让它和超级源点相链接,流入少于流出的我们让它和超级汇点相连接,并且流量就是|流入 - 流出|。

  剩下的,我们就是去跑一个最大流最小费用的算法了。最后统计答案的时候别忘了加上基础的每条边的权值。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 310, maxM = 1e4 + 1210;
int N, M;
ll ans, In_det_Out[maxN] = {0};
struct Graph
{
    int head[maxN], cnt;
    struct Eddge
    {
        int nex, u, v; ll flow, cost;
        Eddge(int a=0, int b=0, int c=0, ll d=0, ll f=0):nex(a), u(b), v(c), flow(d), cost(f) {}
    } edge[maxM];
    inline void addEddge(int u, int v, ll f, ll w)
    {
        edge[cnt] = Eddge(head[u], u, v, f, w);
        head[u] = cnt++;
    }
    inline void _add(int u, int v, ll f, ll w) { addEddge(u, v, f, w); addEddge(v, u, 0, -w); }
    inline void init()
    {
        cnt = 0;
        for(int i=0; i<=N + 1; i++) head[i] = -1;
    }
} Old;
struct MaxFlow_MinCost
{
    int pre[maxN], S, T; ll Flow[maxN], dist[maxN];
    queue<int> Q;
    bool inque[maxN];
    inline bool spfa()
    {
        for(int i=S; i<=T; i++) { pre[i] = -1; dist[i] = INF; inque[i] = false; }
        while(!Q.empty()) Q.pop();
        Q.push(S); dist[S] = 0; inque[S] = true; Flow[S] = INF;
        while(!Q.empty())
        {
            int u = Q.front(); Q.pop(); inque[u] = false;
            ll f, w;
            for(int i=Old.head[u], v; ~i; i=Old.edge[i].nex)
            {
                v = Old.edge[i].v; f = Old.edge[i].flow; w = Old.edge[i].cost;
                if(f && dist[v] > dist[u] + w)
                {
                    dist[v] = dist[u] + w;
                    Flow[v] = min(Flow[u], f);
                    pre[v] = i;
                    if(!inque[v])
                    {
                        inque[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return ~pre[T];
    }
    inline ll EK()
    {
        ll sum_Cost = 0;
        while(spfa())
        {
            int now = T, las = pre[now];
            while(now ^ S)
            {
                Old.edge[las].flow -= Flow[T];
                Old.edge[las ^ 1].flow += Flow[T];
                now = Old.edge[las].u;
                las = pre[now];
            }
            sum_Cost += dist[T] * Flow[T];
        }
        return sum_Cost;
    }
    inline void init()
    {
        S = 0; T = N + 1;
    }
} MF;
inline void init()
{
    ans = 0;
    Old.init(); MF.init();
}
int main()
{
    scanf("%d", &N);
    init();
    for(int i=1, ki, Bi, Ti; i<=N; i++)
    {
        scanf("%d", &ki); In_det_Out[i] -= ki;
        while(ki --)
        {
            scanf("%d%d", &Bi, &Ti); ans += Ti; //下界至少为1,所以要先放入下界
            In_det_Out[Bi]++;
            Old._add(i, Bi, INF, Ti);
        }
    }
    for(int i=1; i<=N; i++)
    {
        if(In_det_Out[i] > 0)   //in > out
        {
            Old._add(MF.S, i, In_det_Out[i], 0);
        }
        else if(In_det_Out[i] < 0)  //in < out
        {
            Old._add(i, MF.T, -In_det_Out[i], 0);
        }
    }
    for(int i=2; i<=N; i++) Old._add(i, 1, INF, 0);
    printf("%lld\n", ans + MF.EK());
    return 0;
}
/*
6
2 2 1 3 2
2 4 3 5 4
2 5 5 6 6
0
0
0
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值