luogu P1251 餐巾计划问题 |费用流

题目描述

一个餐厅在相继的 \(N\) 天里,每天需用的餐巾数不尽相同。假设第 \(i\) 天需要 \(r_i\)​块餐巾(\(i=1,2,...,N\))。餐厅可以购买新的餐巾,每块餐巾的费用为 \(p\) 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;或者送到慢洗部,洗一块需 \(n\) 天(\(n>m\)),其费用为 \(s\) 分(\(s<f\))。

每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。

试设计一个算法为餐厅合理地安排好 \(N\) 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。

输入格式

由标准输入提供输入数据。文件第 1 行有 1 个正整数 \(N\),代表要安排餐巾使用计划的天数。

接下来的 \(N\) 行是餐厅在相继的 \(N\) 天里,每天需用的餐巾数。

最后一行包含5个正整数\(p,m,f,n,s\)\(p\) 是每块新餐巾的费用; \(m\) 是快洗部洗一块餐巾需用天数; \(f\) 是快洗部洗一块餐巾需要的费用; \(n\) 是慢洗部洗一块餐巾需用天数; \(s\) 是慢洗部洗一块餐巾需要的费用。

输出格式

将餐厅在相继的 \(N\) 天里使用餐巾的最小总花费输出


参考题解

1.从原点向每一天晚上连一条流量为当天所用餐巾x,费用为0的边,表示每天晚上从起点获得x条脏餐巾。

2.从每一天早上向汇点连一条流量为当天所用餐巾x,费用为0的边,每天白天,表示向汇点提供x条干净的餐巾,流满时表示第i天的餐巾够用 。

3.从每一天晚上向第二天晚上连一条流量为INF,费用为0的边,表示每天晚上可以将脏餐巾留到第二天晚上(注意不是早上,因为脏餐巾在早上不可以使用)。

4.从每一天晚上向这一天+快洗所用天数t1的那一天早上连一条流量为INF,费用为快洗所用钱数的边,表示每天晚上可以送去快洗部,在地i+t1天早上收到餐巾 。

5.同理,从每一天晚上向这一天+慢洗所用天数t2的那一天早上连一条流量为INF,费用为慢洗所用钱数的边,表示每天晚上可以送去慢洗部,在地i+t2天早上收到餐巾 。

6.从起点向每一天早上连一条流量为INF,费用为购买餐巾所用钱数的边,表示每天早上可以购买餐巾 。 注意,以上6点需要建反向边!3~6点需要做判断(即连向的边必须<=n)

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
inline int read(){
    int f=1,c=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();}
    return f*c;
}
const int N=1e4+10,M=2e5+10,inf=0x3f3f3f3f3f3f3f3f;
int s,t;
int nxt[M],head[N],go[M],edge[M],cost[M],cur[N],tot=1;
inline void add(int u,int v,int o1,int o2){
    nxt[++tot]=head[u],head[u]=tot,go[tot]=v,edge[tot]=o1,cost[tot]=o2;
    nxt[++tot]=head[v],head[v]=tot,go[tot]=u,edge[tot]=0,cost[tot]=-o2;    
}
int dis[N],ret;
bool vis[N];
inline bool spfa(){
    memset(dis,0x3f,sizeof(dis)); dis[s]=0;
    queue<int>q; q.push(s);
    while(q.size()){
        int u=q.front(); q.pop(); vis[u]=0;
        for(int i=head[u];i;i=nxt[i]){
            int v=go[i];
            if(edge[i]&&dis[v]>dis[u]+cost[i]){
                dis[v]=dis[u]+cost[i];
                if(!vis[v])q.push(v),vis[v]=1;
            }
        }
    }
    return dis[t]!=inf;
}
int dinic(int u,int flow){
    if(u==t)return flow;
    int rest=flow,k;
    vis[u]=1;
    for(int i=head[u];i&&rest;i=nxt[i]){
        int v=go[i];
        if(!vis[v]&&edge[i]&&dis[v]==dis[u]+cost[i]){
            k=dinic(v,min(rest,edge[i]));
            if(!k)dis[v]=-1;
            ret+=k*cost[i];
            edge[i]-=k;
            edge[i^1]+=k;
            rest-=k;
        }
    }
    vis[u]=0;
    return flow-rest;
}
int P,F,S;
signed main(){
    int N=read(); t=2*N+3;
    for(int i=1,x;i<=N;i++){
        x=read();
        add(s,i,x,0);
        add(i+N,t,x,0);
    } 
    int m=read(),t1=read(),m1=read(),t2=read(),m2=read();
    for(int i=1;i<=N;i++){
        if(i+1<=N) add(i,i+1,inf,0);
        if(i+t1<=N) add(i,i+N+t1,inf,m1);
        if(i+t2<=N) add(i,i+N+t2,inf,m2);
        add(s,i+N,inf,m);
    }
    int flow=0,maxflow=0;
    while(spfa())
    while(flow=dinic(s,inf))maxflow+=flow;
    cout<<ret<<endl;
}

猜你喜欢

转载自www.cnblogs.com/naruto-mzx/p/12206080.html