[网络流24题]餐巾计划问题——最小费用最大流

题目大意:

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

思路:

典型的一道最小费用最大流的题目,最大流显然是每一天的餐巾都要准备好的,然后在这个基础上求解最小费用。每一天的餐巾有三个来源,快洗慢洗和重新买,如果要快洗或者是慢洗的话显然就是要从之前的餐巾转移过来。每一天剩下来的餐巾在一边建点,每一天需要的餐巾在另一边建点。源点连接剩下的餐巾限制流量,汇点连接每天需要的餐巾保证上限。
我一开始是这样建边的,每一天剩下来的餐巾可以向右边所有满足条件的点连边,所以最差情况下变的个数会有n2个,然后就TLE了。。
另外一个很优秀的方法就是相邻的两天剩下来的餐巾之间互相连边,表示餐巾先留下来不洗,等到到了要用的时候再去洗,这样把变的个数大大减小了,没想到跑的这么快。

/*=========================
 * Author : ylsoi
 * Problem : luogu1251
 * Algrithm : mcmf 
 * Time : 2018.7.27
 * =======================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("luogu1251_modify.in","r",stdin);
    freopen("luogu1251_modify.out","w",stdout);
}

const int maxn=2000+10;
const int maxe=maxn*maxn;
const int inf=0x3f3f3f3f;
int ss,tt,N,m,n,r[maxn],p,f,s;
int las[maxe<<1],beg[maxn<<1],to[maxe<<1],cnte=1,flow[maxe<<1],cost[maxe<<1];
ll ans,sum;

void add(int u,int v,int fl,int c){
    las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; flow[cnte]=fl; cost[cnte]=c;
    las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u; flow[cnte]=0; cost[cnte]=-c;
}

struct mcmf{
    int dis[maxn<<1];
    int cur[maxn<<1];
    bool vis[maxn<<1];
    deque<int>qu;
    bool spfa(){
        memset(dis,63,sizeof(dis)); dis[ss]=0;
        qu.push_back(ss);
        while(qu.size()){
            int u=qu.front();
            while(dis[u]>ceil(sum*1.0/qu.size())){
                qu.push_back(u);
                qu.pop_front();
            }
            qu.pop_front(); vis[u]=0;
            for(int i=beg[u];i;i=las[i]){
                if(flow[i] && dis[u]+cost[i]<dis[to[i]]){
                    dis[to[i]]=dis[u]+cost[i];
                    if(!vis[to[i]]){
                        vis[to[i]]=1;
                        sum+=dis[to[i]];
                        if(!qu.size() || dis[qu.back()]>dis[to[i]])
                            qu.push_front(to[i]);
                        else qu.push_back(to[i]);
                    }
                }
            }
        }
        return dis[tt]!=inf;
    }
    int dfs(int u,int gap){
        if(u==tt || !gap)return gap;
        vis[u]=1;
        int ret=0,fl;
        for(int &i=cur[u];i;i=las[i]){
            if(vis[to[i]])continue;
            if(dis[to[i]]!=dis[u]+cost[i])continue;
            if((fl=dfs(to[i],min(gap,flow[i])))){
                gap-=fl;
                ret+=fl;
                flow[i]-=fl;
                flow[i^1]+=fl;
            }
            if(!gap)break;
        }
        vis[u]=0;
        return ret;
    }
    void work(){
        while(spfa()){
            REP(i,ss,tt)cur[i]=beg[i];
            ans+=(ll)dis[tt]*dfs(ss,inf);
        }
    }
}T;

void init(){
    scanf("%d",&N);
    REP(i,1,N)scanf("%d",&r[i]);
    scanf("%d%d%d%d%d",&p,&m,&f,&n,&s);
    ss=0; tt=N<<1|1;
    REP(i,1,N){
        add(N+i,tt,r[i],0);
        add(ss,i,r[i],0);
        add(ss,N+i,inf,p);
        if(i+m<=N)add(i,N+i+m,inf,f);
        if(i+n<=N)add(i,N+i+n,inf,s);
        if(i!=N)add(i,i+1,inf,0);
    }
}

int main(){
    File();
    init();
    T.work();
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值