• 网络流24题 餐巾计划问题


    题目描述

    一个餐厅在相继的 N 天里,每天需用的餐巾数不尽相同。假设第 i 天需要 ri块餐巾(i=1,2,…,N)。餐厅可以购买新的餐巾,每块餐巾的费用为 p 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;或者送到慢洗部,洗一块需 n 天(n>m),其费用为 s<f 分。每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。试设计一个算法为餐厅合理地安排好 N 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划.

    输入输出格式

    输入格式:

    第 1 行有 6 个正整数 N,p,m,f,n,s。N 是要安排餐巾使用计划的天数;p 是每块新餐巾的费用;m 是快洗部洗一块餐巾需用天数;f 是快洗部洗一块餐巾需要的费用;n 是慢洗部洗一块餐巾需用天数;s 是慢洗部洗一块餐巾需要的费用。接下来的 N 行是餐厅在相继的 N 天里,每天需用的餐巾数。

    输出格式:

    程序运行结束时,将餐厅在相继的 N 天里使用餐巾的最小总花费输出

    输入输出样例

    输入样例#1:
    3 10 2 3 3 2
    5
    6
    7
    
    输出样例#1:
    145


    BYVOID:
    【问题分析】
    网络优化问题,用最小费用最大流解决。
    【建模方法】
    把每天分为二分图两个集合中的顶点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(i+m<=N)连一条容量为无穷大,费用为f的有向边。
    6、从每个Xi向Yi+n(i+n<=N)连一条容量为无穷大,费用为s的有向边。
    求网络最小费用最大流,费用流值就是要求的最小总花费。
    【建模分析】
    这个问题的主要约束条件是每天的餐巾够用,而餐巾的来源可能是最新购买,也可能是前几天送洗,今天刚刚洗好的餐巾。每天用完的餐巾可以选择送到快洗部或慢洗部,或者留到下一天再处理。
    经过分析可以把每天要用的和用完的分离开处理,建模后就是二分图。二分图X集合中顶点Xi表示第i天用完的餐巾,其数量为ri,所以从S向Xi连接容量为ri的边作为限制。Y集合中每个点Yi则是第i天需要的餐巾,数量为ri,与T连接的边容量作为限制。每天用完的餐巾可以选择留到下一天(Xi->Xi+1),不需要花费,送到快洗部(Xi->Yi+m),费用为f,送到慢洗部(Xi->Yi+n),费用为s。每天需要的餐巾除了刚刚洗好的餐巾,还可能是新购买的(S->Yi),费用为p。
    在网络上求出的最小费用最大流,满足了问题的约束条件(因为在这个图上最大流一定可以使与T连接的边全部满流,其他边只要有可行流就满足条件),而且还可以保证总费用最小,就是我们的优化目标。



    主要的问题就是想到把要用的餐巾和用过的餐巾分开处理
    拆点得到二分图,考虑两者的来源和去向,增加s和t求最大流


    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int N=2005,M=1e6+5,INF=1e9;
    int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
        return x*f;
    }
    int n,p,fn,fp,sn,sp,r[N],s,t;
    struct edge{
        int v,ne,c,f,w;
    }e[M<<1];
    int cnt,h[N];
    inline void ins(int u,int v,int c,int w){
        cnt++;
        e[cnt].v=v;e[cnt].c=c;e[cnt].f=0;e[cnt].w=w;
        e[cnt].ne=h[u];h[u]=cnt;
        cnt++;
        e[cnt].v=u;e[cnt].c=0;e[cnt].f=0;e[cnt].w=-w;
        e[cnt].ne=h[v];h[v]=cnt;
    }
    void build(){
        s=0;t=n+n+1;
        for(int i=1;i<=n;i++){
            ins(s,i,r[i],0);
            ins(i+n,t,r[i],0);
            ins(s,i+n,INF,p);
            if(i+1<=n) ins(i,i+1,INF,0);
            if(i+fn<=n) ins(i,i+n+fn,INF,fp);
            if(i+sn<=n) ins(i,i+n+sn,INF,sp);
        }
    }
    int d[N],q[N],head,tail,inq[N],pre[N],pos[N];
    inline void lop(int &x){if(x==N)x=1;else if(x==0) x==N-1;}
    bool spfa(){
        memset(d,127,sizeof(d));
        memset(inq,0,sizeof(inq));
        head=tail=1;
        d[s]=0;inq[s]=1;q[tail++]=s;
        pre[t]=-1;
        while(head!=tail){
            int u=q[head++];inq[u]=0;lop(head);
            for(int i=h[u];i;i=e[i].ne){
                int v=e[i].v,w=e[i].w;
                if(d[v]>d[u]+w&&e[i].c>e[i].f){
                    d[v]=d[u]+w;
                    pre[v]=u;pos[v]=i;
                    if(!inq[v]){
                        inq[v]=1;
                        if(d[v]<d[q[head]]) head--,lop(head),q[head]=v;
                        else q[tail++]=v,lop(tail);
                    }
                }
            }
        }
        return pre[t]!=-1;
    }
    int mcmf(){
        int flow=0,cost=0;
        while(spfa()){
            int f=INF;
            for(int i=t;i!=s;i=pre[i]) f=min(f,e[pos[i]].c-e[pos[i]].f);
            flow+=f;cost+=d[t]*f;
            for(int i=t;i!=s;i=pre[i]){
                e[pos[i]].f+=f;
                e[((pos[i]-1)^1)+1].f-=f;
            }
        }
        return cost;
    }
    int main(int argc, const char * argv[]){
        n=read();p=read();fn=read();fp=read();sn=read();sp=read();
        for(int i=1;i<=n;i++) r[i]=read();
        build();
        printf("%d",mcmf());
    }





  • 相关阅读:
    JavaScript中创建对象的几种模式
    Vue学习5:条件渲染
    Vue学习4:class与style绑定
    Vue学习3:计算属性computed与监听器
    Vue学习2:模板语法
    普通递归与优化递归
    new操作符具体干了什么呢?
    各种形式存放token
    用es6的Array.reduce()方法计算一个字符串中每个字符出现的次数
    ES6 对象的解构赋值
  • 原文地址:https://www.cnblogs.com/candy99/p/6127287.html
Copyright © 2020-2023  润新知