码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj3280

时间:2017-08-12 10:32:46      阅读:263      评论:0      收藏:0      [点我收藏+]

标签:lin   print   open   logs   bool   turn   str   容量   dead   

费用流

建图:建立源汇s,t,对于每天建立两个点,live和dead,live向dead连接下界为a[i],上界为a[i],费用为0的边,表示一天需要只a[i]个人,也就是一天必须死a[i]个人,然后源点向第一天连上所有l[i],b[i],每天live连向下一天,容量为inf,费用为0,表示一天没有用过的研究生可以留到下一天,dead连向下一天的dead,表示可以不治疗死人,每天dead同时连向i+day[j]天,容量为inf,费用为q[j],表示可以治疗任意的死人去进行工作,然后跑一个可行流就行了,现在问题在于处理上下界的边,那么对于live和dead,我们把live连向t,表示流过live至少要有下界的流量,费用为0,s连向dead,表示至少有下界流量的流流入dead,费用为live到dead的费用,0,然后live向dead连一条容量为0的边,表示只能最多流过流量为0的流,这条边自然可以省略,然后就看最小费用,和流量是否为a[i]总和

这里不用设立超级源汇是因为超级源汇的意义在于可以给任意点提供无限的流量,如果源汇本来就是提供流量而无其他意义,就不用设立了。上一题原来源点1是原图中的一部分,有自己的意义,所以需要额外的源汇,如果原来的源汇有下界流出或者有自己的意义就得设立新的源汇,否则不用

技术分享
#include<bits/stdc++.h>
using namespace std;
const int N = 510, inf = 0x3f3f3f3f;
struct edge {
    int nxt, to, f, c;
} e[N * 50];
int n, m, k, source, sink, tot, cnt = 1, sum;
int a[N], head[N], d[N], pree[N], prev[N], vis[N], live[N], dead[N], day[N], c[N], l[N], p[N];
inline void link(int u, int v, int f, int c)
{
    e[++cnt].nxt = head[u];
    head[u] = cnt;
    e[cnt].f = f;
    e[cnt].to = v;
    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()
{
    memset(d, -1, sizeof(d));
    d[source] = 0;
    queue<int> q;
    q.push(source);
    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))
        {
            pree[e[i].to] = i;
            prev[e[i].to] = u;
            d[e[i].to] = d[u] + e[i].c;
            if(vis[e[i].to] == 0)
            {
                q.push(e[i].to);
                vis[e[i].to] = 1;
            }
        }
    }
    return d[sink] != -1; 
}
inline int flow()
{
    int ret = 0, ans = 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 += delta;
        ans += delta * d[sink];
    }
    if(ret < sum) return -1;
    return ans;
}
inline void build()
{
    //研究生 i = 1 -> m
    //天: m + 1 -> m + n 
    tot = 0;
    sink = 2 * n + m + 1; 
    for(int i = 1; i <= n; ++i) 
    {
        live[i] = ++tot;
        dead[i] = ++tot;
        insert(source, dead[i], a[i], 0);
        insert(live[i], sink, a[i], 0);
    }
    for(int i = 1; i <= m; ++i) insert(source, live[1], l[i], p[i]);
    for(int i = 1; i <= n; ++i)
    {
        if(i != n) 
        {
            insert(live[i], live[i + 1], inf, 0);
            insert(dead[i], dead[i + 1], inf, 0);
        }
        for(int j = 1; j <= k; ++j)
            if(i + day[j] + 1 <= n) 
                insert(dead[i], live[i + day[j] + 1], inf, c[j]);
    }
}
int main()
{
    int T;
    scanf("%d", &T);
    for(int kase = 1; kase <= T; ++kase)
    {
        memset(head, 0, sizeof(head));
        cnt = 1;
        sum = 0;
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 1; i <= n; ++i) 
        {
            scanf("%d", &a[i]);
            sum += a[i];
        }
        for(int i = 1; i <= m; ++i) scanf("%d%d", &l[i], &p[i]);
        for(int i = 1; i <= k; ++i) scanf("%d%d", &day[i], &c[i]);        
        build();
        int ans = flow();
        printf("Case %d: ", kase);
        if(ans == -1) puts("impossible");
        else printf("%d\n", ans);
    }
    return 0;
}
View Code

 

bzoj3280

标签:lin   print   open   logs   bool   turn   str   容量   dead   

原文地址:http://www.cnblogs.com/19992147orz/p/7348840.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!