【BZOJ1391】Order(CEOI2008)-最小割

测试地址:Order
题目大意: N N 项任务和M种机器,每项任务都有若干道工序,每道工序需要使用一种机器,完成第 i i 项任务的所有工序就能拿到Ci的报酬。对于机器可以租用或者购买,租用时只能用于某一项任务的某一道工序,用于不同位置时租金可能不同。而购买是一次性的,只要买下来,所有用到这台机器加工的工序都能做,而只需付一次的钱。现在给你这些信息,问能得到的最大利润是多少。 1N,M1200 1 ≤ N , M ≤ 1200
做法:本题需要用到最小割。
我们发现直接求最大利润比较困难,所以我们把所有任务的报酬之和看做预期的利润,求最小的损失,也就是成本。
我们知道,损失会产生于三个方面:一是某项任务无法完成,二是在某道工序租用某种机器,三是购买某种机器。于是我们建图,从源点到每项任务连一条容量为报酬的边,从每种机器到汇点连一条容量为价钱的边,从每项任务到各工序所需的机器连一条容量为租金的边。因为我们知道,如果要完成一项任务,我们必须租用或者购买它所需的全部机器,这就等价于从这项任务到汇点不存在路径,否则我们就完不成这项任务,等价于割掉源点到这项任务的边。因此我们意识到要求一个最小割,我们就完成了这一题。注意要加当前弧优化,不然会TLE。
这个东西很像最大权闭合子图的模型,只不过有了租金这一概念,所以原图中的边容量不应为正无穷。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
const int inf=1000000000;
int n,m,sum=0,S,T;
int first[3010]={0},tot=1,cur[3010],lvl[3010];
int h,t,q[3010];
struct edge
{
    int v,next,f;
}e[3000010];

void insert(int a,int b,int f)
{
    e[++tot].v=b,e[tot].next=first[a],e[tot].f=f,first[a]=tot;
    e[++tot].v=a,e[tot].next=first[b],e[tot].f=0,first[b]=tot;
}

void init()
{
    scanf("%d%d",&n,&m);
    S=n+m+1,T=n+m+2;
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        sum+=x;
        insert(S,i,x);
        for(int j=1;j<=y;j++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            insert(i,n+a,b);
        }
    }
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%d",&x);
        insert(n+i,T,x);
    }
}

bool makelevel()
{
    for(int i=1;i<=T;i++)
    {
        lvl[i]=-1;
        cur[i]=first[i];
    }
    lvl[S]=0;
    h=t=1;
    q[1]=S;
    while(h<=t)
    {
        int v=q[h++];
        for(int i=first[v];i;i=e[i].next)
            if (e[i].f&&lvl[e[i].v]==-1)
            {
                lvl[e[i].v]=lvl[v]+1;
                q[++t]=e[i].v;
            }
    }
    return lvl[T]!=-1;
}

int maxflow(int v,int maxf)
{
    if (v==T) return maxf;
    int ret=0,f;
    for(int i=cur[v];i;i=e[i].next)
    {
        if (e[i].f&&lvl[e[i].v]==lvl[v]+1)
        {
            f=maxflow(e[i].v,min(maxf-ret,e[i].f));
            ret+=f;
            e[i].f-=f;
            e[i^1].f+=f;
            if (ret==maxf) break;
        }
        cur[v]=i;
    }
    if (!ret) lvl[v]=-1;
    return ret;
}

void dinic()
{
    int maxf=0;
    while(makelevel()) maxf+=maxflow(S,inf);
    printf("%d",sum-maxf);
}

int main()
{
    init();
    dinic();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值