bzoj3280

Posted 123456

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj3280相关的知识,希望对你有一定的参考价值。

费用流

建图:建立源汇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的主要内容,如果未能解决你的问题,请参考以下文章

bzoj3280: 小R的烦恼

bzoj3280

BZOJ3280: 小R的烦恼

bzoj3280小R的烦恼 费用流

bzoj1221

bzoj 1029: [JSOI2007]建筑抢修 贪心