P1251 餐巾计划问题
题意
\(N\) 天,每天需要 \(r_i\) 个餐巾,用完后变成脏的。可以花费 \(p\) 买一条,或者花费 \(f\) 在 \(m\) 天后获得一条,或者花费 \(s\) 在 \(n\) 天后花费一条,或者留着,求最小花费。
思路
考虑网络流。我们可以将一天拆为两个点,分别为早上和晚上,这样我们就可以区分出哪个点需要送出和哪个点可以存起来了。那么将早上和 \(T\) 连一个 \((r_i,0)\) 代表需要贡献出这么多脏的,然后因为这个会消失吗,所以再连 \(S\) 和晚上 \((r_i,0)\) 表示收到这么多脏的。那么留到第二天晚上就连 \((\inf,0)\),两个花费就对应从晚上连到早上即可。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
typedef long long ll;
const int MAXN=4e3+5;
const ll INFLL=1e17;
const int INF=2e9;
struct node{
int v,w,nxt;
ll c;
}e[MAXN*20];
int head[MAXN],tot=1;
void add_one(int u,int v,int w,int c){
e[++tot]={v,w,head[u],c};
head[u]=tot;
}
void add(int u,int v,int w,int c){
add_one(u,v,w,c);
add_one(v,u,0,-c);
}
int S,T,arc[MAXN];
ll dep[MAXN];
bool vs[MAXN],inq[MAXN];
bool spfa(){
rep(i,S,T){
dep[i]=INFLL;
arc[i]=head[i];
inq[i]=false;
}
queue<int>que;
que.push(S);
dep[S]=0;
while(!que.empty()){
int u=que.front();
que.pop();
inq[u]=false;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(e[i].w&&dep[u]+e[i].c<dep[v]){
dep[v]=dep[u]+e[i].c;
if(!inq[v]){
que.push(v);
}
}
}
}
return dep[T]!=INFLL;
}
ll cost;
int dfs(int u,int inflow){
if(u==T){
return inflow;
}
vs[u]=true;
int outflow=0;
for(int i=arc[u];i;i=e[i].nxt){
arc[u]=i;
if(dep[u]+e[i].c!=dep[e[i].v]||!e[i].w||vs[e[i].v]){
continue;
}
int nf=dfs(e[i].v,min(inflow,e[i].w));
e[i].w-=nf;
e[i^1].w+=nf;
cost+=e[i].c*nf;
outflow+=nf;
inflow-=nf;
if(!inflow){
break;
}
}
vs[u]=false;
return outflow;
}
int dinic(){
int ans=0;
while(spfa()){
ans+=dfs(S,INF);
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int N;
cin>>N;
S=0,T=2*N+1;
rep(i,1,N){
int r;
cin>>r;
add(i,T,r,0);
add(S,N+i,r,0);
}
int p,m,f,n,s;
cin>>p>>m>>f>>n>>s;
rep(i,1,N){
if(i<N){
add(N+i,N+i+1,INF,0);
}
if(i+m<=N){
add(N+i,i+m,INF,f);
}
if(i+n<=N){
add(N+i,i+n,INF,s);
}
add(S,i,INF,p);
}
int flw=dinic();
cout<<cost<<"\n";
return 0;
}

浙公网安备 33010602011771号