考虑上下界网络流
每个点拆分为入点 i n i in_i ini和出点 o u t i out_i outi
新建源点 s s s,新建汇点 t t t
s s s连向每个入点流量为 [ 0 , i n f ] [0,inf] [0,inf],边权 p p p的边,代表买纸巾
每个出点向 t t t连流量 [ 0 , i n f ] [0,inf] [0,inf],边权 0 0 0的边,代表不洗纸巾
o u t i out_i outi向 i n i + n in_{i+n} ini+n连流量 [ 0 , i n f ] [0,inf] [0,inf]边权 s s s的边,代表这天用完的纸巾去慢洗
o u t i out_i outi向 i n i + m in_{i+m} ini+m流量 [ 0 , i n f ] [0,inf] [0,inf],边权 f f f的边,代表这天用完的纸巾去快洗
i n i in_i ini向 o u t i out_i outi连流量 [ r i , i n f ] [r_i,inf] [ri,inf]的边,代表这天至少需要 r i r_i ri的纸巾
然后还有一条边非常容易忘记,就是当纸巾在第 k k k天洗好,其实是可以留到下一天再用的
i n i in_i ini向 i n i + 1 in_{i+1} ini+1连流量 [ 0 , i n f ] [0,inf] [0,inf]边权 0 0 0的边,代表把纸巾留到下一天
如此一来,我们建立了一个有源汇上下界网络图
t t t向 s s s连一条流量 [ 0 , i n f ] [0,inf] [0,inf]边权 0 0 0的边补齐循环流
然后引入 s s , t t ss,tt ss,tt,补齐对图中所有点的流量,由 s s ss ss向 t t tt tt跑一发最小费用流
得到的就是可行流中最小的费用
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 1e9;
const int maxn = 2e5+10;
int in[maxn],out[maxn],N,s,t,ss,tt;
int p,n,m,fval,sval;
struct edge{
int to,nxt,flow,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int l,int r,int w)
{
in[v] +=l, out[u] += l;
d[++cnt] = ( edge ){ v,head[u],r-l,w}, head[u] = cnt;
d[++cnt] = ( edge ){ u,head[v],0,-w},head[v] = cnt;
}
int flow[maxn],pre[maxn],dis[maxn],vis[maxn];
bool spfa(int s,int t)
{
for(int i=0;i<=t;i++) dis[i] = inf, vis[i] = 0;
queue<int>q; dis[s] = 0, flow[s] = inf, q.push( s );
while( !q.empty() )
{
int u = q.front(); q.pop();
vis[u] = 0;
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( d[i].flow&&dis[u]+d[i].w<dis[v] )
{
dis[v] = dis[u]+d[i].w; flow[v] = min( flow[u],d[i].flow); pre[v] = i;
if( !vis[v] ) vis[v] = 1, q.push( v );
}
}
}
return dis[t]!=inf;
}
int dinic(int s,int t)
{
int ans = 0;
while( spfa(s,t) )
{
// cout << "哈哈哈\n";
int x = t, i; ans += flow[t]*dis[t];
while( x!=s )
{
i = pre[x];
d[i].flow -= flow[t], d[i^1].flow += flow[t];
x = d[i^1].to;
}
}
return ans;
}
signed main()
{
cin >> N;
s = N+N+1, t = s+1, ss = t+1, tt = ss+1;
for(int i=1,x;i<=N;i++) cin >> x, add( i,i+N,x,inf,0 );
cin >> p >> m >> fval >> n >> sval;
for(int i=1;i<=N;i++)
{
add(s,i,0,inf,p );//新的餐巾纸
add(i+N,t,0,inf,0 );//流回汇点
if( i<N ) add(i,i+1,0,inf,0);//留给下一天
if( i+m<=N ) add(i+N,i+m,0,inf,fval);//块洗
if( i+n<=N ) add(i+N,i+n,0,inf,sval);//慢洗
}
add(t,s,0,inf,0);//循环流
for(int i=1;i<=t;i++)
{
if( in[i]>out[i] ) add(ss,i,0,in[i]-out[i],0);
else add(i,tt,0,out[i]-in[i],0);
}
cout << dinic(ss,tt);
}