zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    android 监听ListView中的 item 和button
    android 获取当前系统及应用信息(二)
    MotionEvent中getX()和getRawX()的区别
    HITS 算法(Hypertext Induced Topic Selection)
    放之四海皆适用的设计原则(二)
    源自神话的写作要义之英雄之旅
    这就是搜索引擎:核心技术详解
    源自神话的写作要义之英雄
    使用Spinner和setDropDownViewResource
    友好界面menu
  • 原文地址:https://www.cnblogs.com/naruto-mzx/p/12206080.html
Copyright © 2011-2022 走看看