P1251餐厅计划问题(最小费用最大流)

题目描述

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

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

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

输入格式

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

接下来的一行是餐厅在相继的 NN 天里,每天需用的餐巾数。

最后一行包含5个正整数p,m,f,n,sp,m,f,n,s。pp 是每块新餐巾的费用; mm 是快洗部洗一块餐巾需用天数; ff 是快洗部洗一块餐巾需要的费用; nn 是慢洗部洗一块餐巾需用天数; ss 是慢洗部洗一块餐巾需要的费用。

输出格式

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

 

题解:

/*
 * P1251餐巾计划问题
 * 题意:
 * 一个餐厅在相继的n天里,每天需用的餐巾数不同。
 * 假设第i天需要ri块餐巾。
 * 餐厅可以购买新的餐巾,每块餐巾的费用为p分。
 * 或者把旧餐巾送到快洗部,洗一块需要m天,其费用为f分。
 * 或者送到慢洗部,洗一块需要n天,其费用为s分。
 * 每次结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块脏的餐巾送到慢洗部,以及多少块保存起来延期送洗,
 * 但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
 * 输出在N天里使用餐巾的最小总花费。
 * 题解:
 * 最小费用最大流。
 * 首先,拆点,把每天拆成早上和晚上。每天晚上会受到脏餐巾,每天早上又有干净的餐巾。
 * 1.从源点向每天晚上连一条流量为当天所用餐巾x,费用为0的边,表示每天晚上从起点获得x条脏餐巾。
 * 2.从每一天早上向汇点连一条流量为当天所用餐巾x,费用为0的边,每天白天,表示从汇点提供x条干净的餐巾,流满时表示第i天的餐巾够用。
 * 3.每一天晚上向第二天晚上连一条流量为inf,费用为0的边,表示每天晚上可以将脏餐巾留到第二天晚上。
 * 4.每一天晚上向这一天+快洗所用天数那一天早上连一条流量为inf,费用为快洗所用钱数的边,表示每天晚上可以送去快洗部,在第i+t1天早上收到餐巾。
 * 5.同理,每一天晚上向这一天+慢洗所用天数的那一天早上连一条流量为inf,费用为慢洗所用钱数的边,表示每天晚上可以送去慢洗部,在第i+t2天早上收到餐巾。
 * 6.从起点向每天早上连一条流量为inf,费用为购买餐巾所用钱数的边,表示每天早上可以购买餐巾。
 */

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
const ll inf=1e18;
int head[maxn];
int tot;
int s,t;
ll dis[maxn];
int pre[maxn];
int lst[maxn];
ll flow[maxn];
int visit[maxn];
ll maxflow;
ll mincost;
struct node {
    int u,v;
    ll dis,flow;
    int nxt;
}edge[maxn];
void addedge (int u,int v,ll flow,ll dis) {
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].flow=flow;
    edge[tot].dis=dis;
    edge[tot].nxt=head[u];
    head[u]=tot++;
}
bool spfa (int s,int t) {
    for (int i=0;i<maxn;i++) dis[i]=inf,flow[i]=inf,visit[i]=0;
    queue<int> q;
    q.push(s);
    visit[s]=1;
    dis[s]=0;
    pre[t]=-1;
    while (!q.empty()) {
        int u=q.front();
        q.pop();
        visit[u]=0;
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            if (edge[i].flow>0&&dis[edge[i].v]>dis[u]+edge[i].dis) {
                dis[edge[i].v]=dis[u]+edge[i].dis;
                pre[edge[i].v]=u;
                lst[edge[i].v]=i;
                flow[edge[i].v]=min(flow[u],edge[i].flow);
                if (!visit[edge[i].v]) {
                    visit[edge[i].v]=1;
                    q.push(edge[i].v);
                }
            }
        }
    }
    return pre[t]!=-1;
} 
void MCMF () {
    while (spfa(s,t)) {
        int u=t;
        maxflow+=flow[t];
        mincost+=flow[t]*dis[t];
        while (u!=s) {
            edge[lst[u]].flow-=flow[t];
            edge[lst[u]^1].flow+=flow[t];
            u=pre[u];
        }
    }
} 
int a[maxn];
int main () {
    memset(head,-1,sizeof(head));
    int n;
    int p,m,f,_n,_s;
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    scanf("%d%d%d%d%d",&p,&m,&f,&_n,&_s);
    //1-2*n代表每天的早上和晚上
    s=0,t=2*n+1;
    for (int i=1;i<=n;i++) {
        addedge(s,i+n,a[i],0);
        addedge(i+n,s,0,0);
    }
    for (int i=1;i<=n;i++) {
        addedge(i,t,a[i],0);
        addedge(t,i,0,0);
    }
    for (int i=1;i<n;i++) {
        addedge(i+n,i+n+1,inf,0);
        addedge(i+n+1,i+n,0,0);
    }
    for (int i=1;i<=n;i++) {
        if (i+m>n) continue;
        addedge(i+n,i+m,inf,f);
        addedge(i+m,i+n,0,-f);
    }
    for (int i=1;i<=n;i++) {
        if (i+_n>n) continue;
        addedge(i+n,i+_n,inf,_s);
        addedge(i+_n,i+n,0,-_s);
    }
    for (int i=1;i<=n;i++) {
        addedge(s,i,inf,p);
        addedge(i,s,0,-p);
    }
    MCMF();
    printf("%lld\n",mincost);
}

 

posted @ 2020-07-16 14:46  zlc0405  阅读(168)  评论(0编辑  收藏  举报