[网络流24题(2/24)] 餐巾计划问题 (洛谷P1251)最小费用最大流 传送门 题意: 分析: 代码:
题意:
现在有$N$天,第$i$天需要使用$a_i$个新的餐巾,新餐巾用完后,可以花费$f$元使得第$i+m$天后可以重新利用;可以花费$s$元使得第$i+n$天后可以使用;也可以把旧餐巾留到下一天去洗。
现在买一个新的餐巾需要$p$元,现在问你在满足所有天的要求的前提下的最小花费。
分析:
一个比较厉害的最小费用最大流的模型。
看到即涉及物品的(物品还能流动的),又涉及最小费用的往费用流上考虑?
首先需要知道,在这$N$天中,餐巾是会不断的移动的,我们试着把餐巾的移动路线分析一下:
我们发现,对于每一天,存在着旧餐巾和新餐巾两种叠加的状态,因此我们考虑把一天拆成两个点(分别为旧餐巾和新餐巾)分别来考虑。
对于第$i$天的旧餐巾:
1. 它能够走向第$i+1$的旧餐巾处,无花费
2. 它能够通过花费$f$元,重新洗涤,到达第$i+m$天的新餐巾处
3. 它能够通过花费$s$元,重新洗涤,到达第$i+n$天的新餐巾处
对于第$i$天的新餐巾:
1. 它能通过花费$p_i$元,从源点购买$a_i$个全新的餐巾。
2. 它能通过$i-m$天的旧餐巾洗涤过来。
3. 它能通过$i-n$天的旧餐巾洗涤过来。
而我们发现,对于旧餐巾,它每天都是会通过某种途径源源不断的获取$a_i$的旧餐巾(因为每天都会有新餐巾用完变成旧餐巾)。而上述并没有流向旧餐巾的状态,因此我们考虑从源点向每一天的旧餐巾的状态连接一条容量为$a_i$,花费为$0$的边。
同时,我们发现,新餐巾是远远不断的送出餐巾,同理,我们考虑从每天的新餐巾向汇点连接一天容量为$a_i$,花费为$0$的边,这条边代表着每天都会使用掉$a_i$个餐巾。
而按照我们上述建出来的图,显然是满足流量平衡的,因此,当我们使这张图获取到最大流时,即可满足每一天的餐巾需求均能够被满足。
之后我们只需要在这个基础上跑一遍最小费用最大流即是答案。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn =5005;
const int maxm=500005;
const ll inf=0x3f3f3f3f3f3f3f3f;
int head[maxn],cnt=0;
int vis[maxn],sp,ep;
ll maxflow,cost,dis[maxn];
struct Node{
int to,next;
ll val,cost;
}q[maxm<<1];
void init(){
memset(head,-1,sizeof(head));
cnt=2;
maxflow=cost=0;
}
void addedge(int from,int to,ll val,ll cost){
q[cnt].to=to;
q[cnt].next=head[from];
q[cnt].val=val;
q[cnt].cost=cost;
head[from]=cnt++;
}
void add_edge(int from,int to,ll val,ll cost){
addedge(from,to,val,cost);
addedge(to,from,0,-cost);
}
bool spfa(){
memset(vis,0,sizeof(vis));
memset(dis,inf,sizeof(dis));
dis[sp]=0;
vis[sp]=1;
queue<int>que;
que.push(sp);
while(!que.empty()){
int x=que.front();
que.pop();
vis[x]=0;
for(int i=head[x];i!=-1;i=q[i].next){
int to=q[i].to;
if(dis[to]>dis[x]+q[i].cost&&q[i].val){
dis[to]=dis[x]+q[i].cost;
if(!vis[to]){
que.push(to);
vis[to]=1;
}
}
}
}
return dis[ep]!=inf;
}
ll dfs(int x,ll flow){
if(x==ep){
vis[ep]=1;
maxflow+=flow;
return flow;
}//可以到达t,加流
ll used=0;//该条路径可用流量
vis[x]=1;
for(int i=head[x];i!=-1;i=q[i].next){
int to=q[i].to;
if((vis[to]==0||to==ep)&&q[i].val!=0&&dis[to]==dis[x]+q[i].cost){
ll minflow=dfs(to,min(flow-used,q[i].val));
if(minflow!=0){
cost+=q[i].cost*minflow;
q[i].val-=minflow;
q[i^1].val+=minflow;
used+=minflow;
}
//可以到达t,加费用,扣流量
if(used==flow)break;
}
}
return used;
}
ll mincostmaxflow(){
while(spfa()){
vis[ep]=1;
while(vis[ep]){
memset(vis,0,sizeof(vis));
dfs(sp,inf);
}
}
return maxflow;
}
int main()
{
int n,p,m,f,N,s;
scanf("%d",&n);
sp=0,ep=2*n+1;
init();
for(int i=1;i<=n;i++){
int num;
scanf("%d",&num);
add_edge(sp,i+n,num,0);
add_edge(i,ep,num,0);
}
scanf("%d%d%d%d%d",&p,&m,&f,&N,&s);
for(int i=1;i<=n;i++){
if(i+1<=n) add_edge(i+n,i+n+1,inf,0);
if(i+m<=n) add_edge(i+n,i+m,inf,f);
if(i+N<=n) add_edge(i+n,i+N,inf,s);
add_edge(sp,i,inf,p);
}
mincostmaxflow();
printf("%lld
",cost);
return 0;
}