【Luogu 1251】【Codevs 1237】
题意:
一家餐厅计划运营n天(n<=2000),第i天需要ai个餐巾支持运营。新餐巾可以直接购得,也可以由每天使用后的脏餐巾送到清洗部清洗后获得,给出购买的价格,两个清洗部门清洗的时间和价格。求最小花费。
题解:
一开始建图,打算1~n的点表示需要输入的新餐巾,n+1~2*n的点表脏餐巾的处理,但这样很难处理脏餐巾的流出流量控制。
其实网络流对单点的限制可以放在流向汇点的时候控制。
1~n表示每天获取到的脏餐巾,n+1~2*n表示每天需要的新餐巾。
①S向i点连一条流量为ai,费用为0的边。表示每天新产生的脏餐巾
②S向i+n点连一条流量为inf,费用为新餐巾价格的边。表示每天可以直接购买新餐巾。
③i点向i+1点连一条流量为inf,费用为0的边。表示当天的脏餐巾不处理,交给下一天处理。
设一个清洗部要洗x天,洗一条餐巾花费为y
④i点向i+x+n连一条流量为inf,费用为y的边。表示送洗并直接提供给第i+x天。
建图不应该被现实情况束缚,脏餐巾并不需要被新餐巾使用后提供,可以由源点直接提供。这样建图后,脏餐巾的处理和新餐巾的购入都交给图的上半部分处理,每天花费餐巾的数量则有汇点的汇入控制。跑一边最小费用最大流即可。
代码:
//Codevs 1237
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#define MAXN 4000+30
using namespace std;
int n,m;
int a[MAXN];
int Du[MAXN],dis[MAXN],From[MAXN],From_f[MAXN],dl[110930];
vector <int> f[MAXN],flow[MAXN],FB[MAXN],Cost[MAXN];
bool b[MAXN];
double Ans;
void ADD(int x,int y,int z,int cost)
{
f[x].push_back(y);flow[x].push_back(z);FB[x].push_back(Du[y]);Cost[x].push_back(cost);
f[y].push_back(x);flow[y].push_back(0);FB[y].push_back(Du[x]);Cost[y].push_back(-cost);
Du[x]++;Du[y]++;
}
bool SPFA(int BEGIN,int END)
{
memset(dis,0x5f,sizeof(dis));
int t=0,w=1,x,X;
dis[BEGIN]=0;dl[1]=BEGIN;
do
{
x=dl[++t];
b[x]=false;
for(int i=0;i<Du[x];i++)
{
X=f[x][i];
if(dis[X]>dis[x]+Cost[x][i]&&flow[x][i])
{
dis[X]=dis[x]+Cost[x][i];
From[X]=x;From_f[X]=i;
if(!b[X])
{
dl[++w]=X;
b[X]=true;
}
}
}
}while(t!=w);
if(dis[END]==0x5f5f5f5f) return false;
return true;
}
long long ZG(int BEGIN,int END)
{
int x,X,MFLOW=0x7fffffff,i;
x=END;
while(x!=BEGIN)
{
X=From[x],i=From_f[x];
MFLOW=min(MFLOW,flow[X][i]);
x=X;
}
x=END;
while(x!=BEGIN)
{
X=From[x],i=From_f[x];
flow[X][i]-=MFLOW;
flow[x][FB[X][i]]+=MFLOW;
x=X;
}
long long Re=1;
Re=Re*dis[END]*MFLOW;
return Re;
}
long long Solve(int x,int y)
{
long long Count=0;
while(SPFA(x,y))
Count+=ZG(x,y);
return Count;
}
int main()
{
int S,T;
scanf("%d",&n);
S=0;T=2*n+1;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int New,x,y,xx,yy;
scanf("%d%d%d%d%d",&New,&x,&y,&xx,&yy);
for(int i=1;i<=n;i++)
{
ADD(S,i,a[i],0);
ADD(i+n,T,a[i],0);
ADD(S,i+n,a[i],New);
if(i!=n) ADD(i,i+1,0x3fffffff,0);
if(i+x<=n) ADD(i,i+x+n,0x3fffffff,y);
if(i+xx<=n) ADD(i,i+xx+n,0x3fffffff,yy);
}
cout<< Solve(S,T);
return 0;
}