「网络流 24 题」餐巾计划

本文介绍了一种基于最小费用最大流算法的餐巾使用计划方案。通过拆分节点和建立适当的边来解决餐厅连续多天内餐巾使用、清洗及购买的问题,确保满足每日需求的同时最小化总成本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个餐厅在相继的nnn天里,每天需用的餐巾数不尽相同。假设第iii天需要rir_iri块餐巾。餐厅可以购买新的餐巾,每块餐巾的费用为 PPP分;或者把旧餐巾送到快洗部,洗一块需 MMM天,其费用为FFF分;或者送到慢洗部,洗一块需NNN天,其费用为SSS(S<F)(S<F)S<F
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好nnn天中餐巾使用计划,使总的花费最小。
数据范围:1≤n≤10001≤n≤10001n1000

显然是最小费用最大流
我们考虑拆点,把每一天拆成早上和晚上
源点SSS向第iii天早上连一条流量为infinfinf,费用为ppp的边,表示每天早上购买若干条餐巾,每条ppp
iii天早上向汇点TTT连一条流量为rir_iri,费用为000的边,表示这一天用了rir_iri块餐巾
源点SSS向第iii天晚上连一条流量为rir_iri,费用为000的边,表示这一天结束后剩下rir_iri块脏毛巾
iii天晚上向第i+1i+1i+1天晚上连一条流量为infinfinf,费用为000的边,表示把若干块脏毛巾留到下一天晚上
iii天晚上向第i+Mi+Mi+M天早上连一条流量为infinfinf,费用为FFF的边,表示第iii天晚上把若干块脏毛巾送到快洗部去洗,过MMM天后送回来。
iii天晚上向第i+Ni+Ni+N天早上连一条流量为infinfinf,费用为SSS的边,表示第iii天晚上把若干块脏毛巾送到慢洗部去洗,过NNN天后送回来。
然后就是愉快的最小费用最大流啦!
如果有误在评论区吼一声哦!
代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll inf=0x7fffffffffffffff;
int n,S,T,l,r,p,df,cf,ds,cs,a[4010],q[4010],inque[4010],pre[4010],tot=1,head[4010],nxt[3200010],to[3200010];
ll ans_flow,ans_cost,d[4010],flw[3200010],cst[3200010];
void add_edge(int u,int v,ll ct,ll fw){
    nxt[++tot]=head[u];
    to[tot]=v;
    flw[tot]=fw;
    cst[tot]=ct;
    head[u]=tot;
    return;
}
void Add_edge(int u,int v,ll ct,ll fw){
	add_edge(u,v,ct,fw);
	add_edge(v,u,-ct,0);
	return;
}
bool spfa(){
    for(int i=1;i<=(n<<1)+1;i++)
        d[i]=inf;
    l=r=d[S]=0;
    q[r++]=S;
    inque[S]=1;
    while(l!=r){
        int u=q[l];
        l=(l+1)%5010;
        inque[u]=0;
        for(int i=head[u];i;i=nxt[i]){
            int v=to[i];
            ll ct=cst[i],fw=flw[i];
            if(fw&&ct+d[u]<d[v]){
                d[v]=d[u]+ct;
                pre[v]=i;
                if(!inque[v]){
                    inque[v]=1;
                    q[r]=v;
                    r=(r+1)%5010;
                }
            }
        }
    }
    if(d[T]==inf)
        return 0;
    ll _flow=inf;
    for(int i=T;i!=S;i=to[pre[i]^1])
        _flow=min(_flow,flw[pre[i]]);
    for(int i=T;i!=S;i=to[pre[i]^1]){
        flw[pre[i]]-=_flow;
        flw[pre[i]^1]+=_flow;
    }
    ans_flow+=_flow;
    ans_cost+=d[T]*_flow;
    return 1;
}
void MCMF(){
    while(spfa());
    return;
}
int main(){
    scanf("%d%d%d%d%d%d",&n,&p,&df,&cf,&ds,&cs);
    S=0;
    T=(n<<1)+1;
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        Add_edge(i,T,0,x);
        Add_edge(S,i+n,0,x);
    }
    for(int i=1;i<=n;i++){
    	Add_edge(S,i,p,inf);
        if(i+1<=n)
        	Add_edge(i+n,i+n+1,0,inf);
        if(i+df<=n)
			Add_edge(i+n,i+df,cf,inf);
        if(i+ds<=n)
			Add_edge(i+n,i+ds,cs,inf);
    }
    MCMF();
    printf("%lld",ans_cost);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值