【网络流24题】餐巾计划问题

【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;
}

猜你喜欢

转载自blog.csdn.net/weixin_42789801/article/details/81776189
今日推荐