[网络流24题] 10.餐巾计划问题 解题报告 (最小费用最大流)

10.餐巾计划问题

题意

一间餐厅需要连续运营 \(N\) 天, 每天都有不同的餐巾需求量 \(r[i]\),

每天可以有 4 种操作,

  1. 购买新的餐巾, 每条餐巾的费用为 \(p\).
  2. 将用过的餐巾保存, 留到下一天.
  3. 将用过的餐巾送到快洗部, \(m\) 天后拿到餐巾, 每条餐巾的清洗费用为 \(f\).
  4. 将用过的餐巾送到慢洗部, \(n\) 天后拿到餐巾, 每条餐巾的清洗费用为 \(s\).

求满足每天餐巾需求的总费用最小值.


思路

每天都需要有 \(r[i]\) 条餐巾, 那么考虑从节点 \(i\)\(T\) 连一条容量为 \(r[i]\) 的边, 那我们的大致思路就确定为了 最小费用最大流.

再考虑每天需要的餐巾可以从哪里获得,

  1. 新购买.
  2. \(i-m\) 花费 $f/ $条 的代价获得.
  3. \(i-n\) 花费 \(s/\)条 的代价获得.

那我们再考虑把每一天拆成 供给点需求点, 也可以理解为这一天的 结束开始.

\(i\) 天的供给点为 \(i\), 需求点为 \(i+\).N

每个供给点 \(i\) 就需要往外连三条边, 分别对应题意中的后三种操作,

  1. 连向 \(i+1\), 容量为 \(inf\), 边权为 \(0\), 表示把用过的毛巾留到下一天.
  2. 连向 \(i+m+N\), 容量为 \(inf\), 边权为 \(f\), 表示把用过的毛巾送到快洗部.
  3. 连向 \(i+n+N\), 容量为 \(inf\), 边权为 \(s\), 表示把用过的毛巾送到慢洗部.

除此之外, 我们还需要新建一个供给点, 向所有需求点连容量为 \(inf\) ,边权为 \(p\) 的边, 表示当天新购买的毛巾.

图建完后, 跑个费用流即可. (费用流 -- OI Wiki)


代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int _=1e5+7;
const int __=1e7+7;
const ll inf=1e17;
int n,p,fd,fc,sd,sc;  // fd,fc,sd,sc 分别对应题目中的 m,f,n,s
int S,T,apl,lst[_],nxt[__],to[__],tot=1;
ll dis[_],w[__],c[__],ans;
bool vis[_];
queue<int> q;
void add(int x,int y,ll cap,ll wgt){
  nxt[++tot]=lst[x]; to[tot]=y; c[tot]=cap; w[tot]=wgt; lst[x]=tot;
  nxt[++tot]=lst[y]; to[tot]=x; c[tot]=0; w[tot]=-wgt; lst[y]=tot;
}
void init(){
  cin>>n; int nd;
  S=2*n+1,T=2*n+2,apl=2*n+3;
  for(int i=1;i<=n;i++){
    scanf("%d",&nd);
    add(S,i,(ll)nd,0);
    add(i+n,T,(ll)nd,0);
    add(apl,i+n,inf,0);
  }
  scanf("%d%d%d%d%d",&p,&fd,&fc,&sd,&sc);
  add(S,apl,inf,p);
  for(int i=1;i<=n;i++){
    if(i<n) add(i,i+1,inf,0);
    if(i+fd<=n) add(i,i+fd+n,inf,fc);
    if(i+sd<=n) add(i,i+sd+n,inf,sc);
  }
}
bool bfs(){
  for(int i=1;i<=apl;i++) dis[i]=inf;
  memset(vis,0,sizeof(vis));
  while(!q.empty()) q.pop();
  dis[S]=0; q.push(S);
  while(!q.empty()){
    int u=q.front(); q.pop();
    vis[u]=0;
    for(int i=lst[u];i;i=nxt[i]){
      int v=to[i];
      if(dis[v]<=dis[u]+w[i]||!c[i]) continue;
      dis[v]=dis[u]+w[i];
      if(!vis[v]){
	q.push(v);
	vis[v]=1;
      }
    }
  }
  return dis[T]!=inf;
}
ll dfs(int u,ll flow){
  if(u==T) return flow;
  ll rest=flow; vis[u]=1;
  for(int i=lst[u];i;i=nxt[i]){
    int v=to[i];
    if(vis[v]||dis[v]!=dis[u]+w[i]||!c[i]) continue;
    ll cst=dfs(v,min(rest,c[i]));
    if(cst==0) dis[v]=-inf;
    else{
      c[i]-=cst; c[i^1]+=cst;
      rest-=cst; ans+=cst*w[i];
    }
  }
  vis[u]=0;
  return flow-rest;
}
void Dinic(){
  ll flow;
  while(bfs()){
    do{
      flow=dfs(S,inf);
    }while(flow);
  }
}
int main(){
#ifndef ONLINE_JUDGE
  freopen("x.in","r",stdin);
#endif
  init();
  Dinic();
  printf("%lld\n",ans);
  return 0;
}
posted @ 2020-01-17 10:29  BruceW  阅读(144)  评论(0编辑  收藏  举报