P1251 餐巾计划问题
看到数据很小,且 \(DP\) 很难记录状态的题大概就是网络流量了,可以用网络流想一下
首相考虑费用流的特性,流量会流满,利用这一点,我们可以满足每天需要的餐巾纸都能被提供
考虑餐巾纸用过后会变成脏餐巾纸,利用流量的特性将没洗的留给第二天,但是这有出现了一个问题,每天,既用卫生纸,又产生脏卫生纸,这两种纸肯定不能和在一起,而且如果设用纸巾是用掉流量的话,就需要 \(S\) 再连入额外的流量
这时一个点已经无法处理了,因为每天用多少纸是固定的,所以将一个点拆成两个,早上和晚上,早上用纸,晚上产生纸和处理洗纸,对应到图上是这样的
- 一个点分成早上和晚上 \(x, x'\)
- 每天可以购买新纸,\(S \to x\)
- 必需要用纸 \(x \to T\)
- 产生脏纸 \(S \to x'\)
- 脏纸留给明天 \(x' \to (x+1)'\)
- 清洗脏纸 \(x' \to (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;
}