BZOJ 1221: [HNOI2001] 软件开发|费用流

把每天分为二分图两个集合中的顶点Xi,Yi,建立附加源S汇T。

1、从S向每个Xi连一条容量为ri,费用为0的有向边。

2、从每个Yi向T连一条容量为ri,费用为0的有向边。

3、从S向每个Yi连一条容量为无穷大,费用为p的有向边。

4、从每个Xi向Xi+1(i+1<=N)连一条容量为无穷大,费用为0的有向边。

5、从每个Xi向Yi+m+1(i+m+1<=N)连一条容量为无穷大,费用为f的有向边。

6、从每个Xi向Yi+n+1(i+n+1<=N)连一条容量为无穷大,费用为s的有向边。

求网络最小费用最大流,费用流值就是要求的最小总花费。

感觉费用流好神奇

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#include<iostream>
#define MX 200000000
using namespace std;
int sc()
{
    int i=0; char c=getchar();
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i;
}
bool inq[2222];
int dis[2222],q[2222],from[2222],p[2222];
int head[2222],lst[55555],nxt[55555],c[55555],v[55555];
int n,a,b,fa,f,fb,tot=1;
int S,T,ans;
void add(int x,int y,int a,int b)
{
    lst[++tot]=y;
    c[tot]=a;
    v[tot]=b;
    nxt[tot]=head[x];
    head[x]=tot;
}
void insert(int a,int b,int c,int d){add(a,b,c,d);add(b,a,0,-d);}
bool spfa()
{
    for(int i=1; i<=T; i++) dis[i]=MX; dis[S]=0;
    int l=1,r=2; q[1]=S;
    while(l!=r)
    {
        int x=q[l++];l%=2200;inq[x]=0;
        for(int i=head[x];i;i=nxt[i])
            if(c[i]&&dis[x]+v[i]<dis[lst[i]])
            {
                dis[lst[i]]=dis[x]+v[i];
                from[lst[i]]=x;p[lst[i]]=i;
                if(!inq[lst[i]])q[r++]=lst[i],r%=2200,inq[lst[i]]=1;
            }
    }
    //cout << dis[T]<<" "<< ans << endl;
    return dis[T]!=MX;
}
void mcf()
{
    int cc=MX,vv=0;
    for(int i=T;i!=S;i=from[i]) cc=min(cc,c[p[i]]);
    for(int i=T;i!=S;i=from[i])
    {
        c[p[i]]-=cc,c[p[i]^1]+=cc;
        ans+=cc*v[p[i]];
    }
}   
int main()
{
    n=sc(),a=sc(),b=sc(),f=sc(),fa=sc(),fb=sc();
    S=2*n+1,T=S+1;
    for(int i=1; i<=n; i++)
    {
        int x=sc();
        insert(S,i,x,0);
        insert(i+n,T,x,0);
        insert(S,i+n,MX,f);
        if(i+a+1<=n)insert(i,i+n+a+1,MX,fa);
        if(i+b+1<=n)insert(i,i+n+b+1,MX,fb);
        if(i+1<=n)insert(i,i+1,MX,0);
    }
    while(spfa())mcf();
    cout << ans;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值