P1251 餐巾计划问题

看到数据很小,且 (DP) 很难记录状态的题大概就是网络流量了,可以用网络流想一下

首相考虑费用流的特性,流量会流满,利用这一点,我们可以满足每天需要的餐巾纸都能被提供

考虑餐巾纸用过后会变成脏餐巾纸,利用流量的特性将没洗的留给第二天,但是这有出现了一个问题,每天,既用卫生纸,又产生脏卫生纸,这两种纸肯定不能和在一起,而且如果设用纸巾是用掉流量的话,就需要 (S) 再连入额外的流量

这时一个点已经无法处理了,因为每天用多少纸是固定的,所以将一个点拆成两个,早上和晚上,早上用纸,晚上产生纸和处理洗纸,对应到图上是这样的

  • 一个点分成早上和晚上 (x, x')
  • 每天可以购买新纸,(S o x)
  • 必需要用纸 (x o T)
  • 产生脏纸 (S o x')
  • 脏纸留给明天 (x' o (x+1)')
  • 清洗脏纸 (x' o (x + a))
#include<bits/stdc++.h>
using namespace std;
#define rg register
inline int read(){
	rg char ch=getchar();
	rg int x=0,f=0;
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
const int N=4005,M=N<<3;
int head[N],ver[M],nxt[M],edge[M],flow[M],tot=1;
inline void add(int x,int y,int z,int f){
	ver[++tot]=y;
	edge[tot]=z;
	flow[tot]=f;
	nxt[tot]=head[x];
	head[x]=tot;
}
inline void adds(int x,int y,int z,int f){
	add(x,y,z,f);
	add(y,x,-z,0);
}
int q;
int nd[N];
int P,MM,F,NN,S;
int s,t;
int dis[N],pre[N],vis[N];
#define inf 0x3f3f3f3f
inline int spfa(){
	queue<int> q;
	q.push(s);
	memset(dis,0x3f,sizeof dis);
	memset(vis,0,sizeof vis);
	dis[s]=0;
	while(!q.empty()){
		int x=q.front();q.pop();
		vis[x]=false;
		for(int y,i=head[x];i;i=nxt[i]){
			y=ver[i];
			if(dis[y]>dis[x]+edge[i]&&flow[i]){
				dis[y]=dis[x]+edge[i];
				pre[y]=i;
				if(!vis[y]){
					vis[y]=true;
					q.push(y);
				}
			}
		}
	}
	return dis[t]^0x3f3f3f3f;
}
template <typename T>
inline void ckmin(T &a,const T &b){
	if(a>b) a=b;
}
inline long long EK(){
	int minflow;
	long long ans=0;
	while(spfa()){
		minflow=0x3f3f3f3f;
		for(int i=pre[t];i;i=pre[ver[i^1]])
			ckmin(minflow,flow[i]);
		ans+=dis[t]*minflow;
		for(int i=pre[t];i;i=pre[ver[i^1]])
			flow[i]-=minflow,flow[i^1]+=minflow;
	}
	return ans;
}
signed main(){
	q=read();
	s=0,t=q<<1|1;
	for(int i=1;i<=q;++i) nd[i]=read(),adds(s,i<<1,0,nd[i]);
	P=read(),MM=read(),F=read(),NN=read(),S=read();
	for(int i=1;i<=q;++i){
		adds(s,(i<<1)-1,P,nd[i]);
		adds((i<<1)-1,t,0,nd[i]);
		if(i+MM<=q) adds(i<<1,(i+MM<<1)-1,F,inf);
		if(i+NN<=q) adds(i<<1,(i+NN<<1)-1,S,inf);
		if(i^q) adds(i<<1,i+1<<1,0,inf);
	}
	cout<<EK()<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/XiaoVsun/p/13098532.html