[网络流24题]洛谷 P1251 餐巾计划问题(费用流)

传送门


把每一天拆成早上(i)和晚上(i+n),每天晚上会收到用过的脏毛巾,早上会从快洗部和慢洗部或通过购买得到新毛巾。

构图:
1.从源点向每一天晚上建流量为当天需要的餐巾数ri,费用为0的边,表示每天晚上会有ri条用过的餐巾。
2.从每一天早上向汇点建流量为当天需要的餐巾数ri,费用为0的边,表示每天白天需要提供ri条干净的餐巾,流满时餐巾够用。
3.从每一天晚上向下一天晚上建流量为INF,费用为0的边,表示每一天晚上的脏餐巾可以留到下一天晚上再处理。
4.从每一天晚上向当天+快洗需要的天数建流量为INF,费用为快洗价格的边,表示每天晚上可以送餐巾去快洗部,在当天+快洗需要的天数那一天收到干净餐巾。
5.从每一天晚上向当天+慢洗需要的天数建流量为INF,费用为慢洗价格的边,表示每天晚上可以送餐巾去慢洗部,在当天+慢洗需要的天数那一天收到干净餐巾。
6.从源点向每一天早上建边,流量为INF,费用为购买价格,表示每天早上可以购买餐巾。
后四条建边时注意天数不能超过n。

建图后跑费用流。


Code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#define ll long long
using namespace std;

const int INF=0x3f3f3f3f;
struct node{int x,y,c,d,next,other;}a[24010];
int first[4010],f[4010],pre[4010],pos[4010],p[4010];
int n,m,t1,m1,t2,m2,st,ed,len;
bool v[4010];
queue<int> q;

void ins(int x,int y,int c,int d)
{
    a[++len]=(node){x,y,c,d,first[x],len+1};first[x]=len;
    a[++len]=(node){y,x,0,-d,first[y],len-1};first[y]=len;
}

bool spfa()
{
    memset(f,INF,sizeof(f));f[st]=0;
    memset(v,false,sizeof(v));v[st]=true;
    p[st]=INF;
    q.push(st);
    while(!q.empty())
    {
        int x=q.front();
        for(int i=first[x];i;i=a[i].next)
        {
            int y=a[i].y;
            if(a[i].c>0 && f[y]>f[x]+a[i].d)
            {
                f[y]=f[x]+a[i].d;
                pre[y]=i;
                pos[y]=x;
                p[y]=min(p[x],a[i].c);
                if(!v[y])
                {
                    v[y]=true;
                    q.push(y);
                }
            }
        }
        v[x]=false;
        q.pop();
    }
    return f[ed]<INF;
}

ll flow()
{
    ll ans=0;
    while(spfa())
    {
        ans+=f[ed]*p[ed];
        for(int i=ed;i!=st;i=pos[i])
        {
            a[pre[i]].c-=p[ed];
            a[a[pre[i]].other].c+=p[ed];
        }
    }
    return ans;
}

int main()
{
    scanf("%d",&n);
    st=2*n+1;ed=st+1;
    memset(first,len=0,sizeof first);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        ins(st,i+n,x,0);
        ins(i,ed,x,0);
    }
    scanf("%d %d %d %d %d",&m,&t1,&m1,&t2,&m2);
    for(int i=1;i<=n;i++)
    {
        if(i+1<=n) ins(i+n,i+n+1,INF,0);
        if(i+t1<=n) ins(i+n,i+t1,INF,m1);
        if(i+t2<=n) ins(i+n,i+t2,INF,m2);
        ins(st,i,INF,m);
    }
    printf("%lld",flow());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值