首页 > 代码库 > bzoj3876

bzoj3876

有上下界的费用流

建图:每条边连接两个节点,边的费用为花费时间,下界为1,上界正无穷,源点为1,所有点连向1,下界0,上界正无穷,表示随时可以返回,于是我们想求一个最小费用可行流

具体做法是先建立超级源汇,对于每条有上下界的边(u,v),u->t,流量为u的入度,费用为0,s->v,流量为下界1,费用为(u,v)费用,u->v连边,流量inf,花费为(u,v)的花费,这样强迫流量流入流出,流满了下界

其他边和之前一样连

u->t的目的是让流入u的流量至少流过到v的边的下界,这样不会让流到u的流量小于下界,s->v的边的目的是强制流入v点至少下界的流量,花费为原来边的花费,这样保证了对于一条边(u,v),流入u的流量不少于下界,从v流出的流量至少为下界,且花费为(u,v)的花费。保证对于点u,流入u的流量至少为所有u的出边的下界之和,这样流入u,也就是流入u出边的下界可以被满足,然后点v满足了到v边下界的流量肯定能流入v,且花费分别为所有流入v的边

技术分享
#include<bits/stdc++.h>
using namespace std;
const int N = 10010, inf = 1 << 29;
struct edge {
    int nxt, to, f, c;
} e[N << 2];
int n, cnt = 1, source, sink;
int head[N], d[N], pree[N], prev[N], vis[N];
inline void link(int u, int v, int f, int c)
{
    e[++cnt].nxt = head[u];
    head[u] = cnt;
    e[cnt].to = v;
    e[cnt].f = f;
    e[cnt].c = c;
}
inline void insert(int u, int v, int f, int c)
{
    link(u, v, f, c);
    link(v, u, 0, -c);
}
bool spfa()
{
    queue<int> q;
    memset(d, -1, sizeof(d));
    q.push(source);
    d[source] = 0;
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = head[u]; i; i = e[i].nxt) if(e[i].f && (d[e[i].to] > d[u] + e[i].c || d[e[i].to] == -1))
        {
            d[e[i].to] = d[u] + e[i].c;
            pree[e[i].to] = i;
            prev[e[i].to] = u;
            if(vis[e[i].to] == 0)
            {
                vis[e[i].to] = 1;
                q.push(e[i].to);
            }
        } 
    }
    return d[sink] != -1;
}
int Edmonds_Karp()
{
    int ret = 0;
    while(spfa())
    {
        int now = sink, delta = inf;
        while(now != source)
        {
            delta = min(delta, e[pree[now]].f);
            now = prev[now];
        }
        now = sink;
        while(now != source)
        {
            e[pree[now]].f -= delta;
            e[pree[now] ^ 1].f += delta;
            now = prev[now];
        }
        ret += d[sink] * delta;
    }
    return ret;
}
int main()
{
    scanf("%d", &n);
    sink = n + 1;
    for(int i = 1; i <= n; ++i)
    {
        int m, b, t;
        scanf("%d", &m);
        insert(i, sink, m, 0);
        while(m--)
        {
            scanf("%d%d", &b, &t);
            insert(source, b, 1, t);
            insert(i, b, inf, t);
        }
        if(i != 1) insert(i, 1, inf, 0);    
    }
    printf("%d\n", Edmonds_Karp());
    return 0;
}
View Code

 

bzoj3876