餐巾计划问题
分析
奇妙的拆点用法。
分析完题目后,我们发现每一天需要\(r_i\)块新毛巾,并产生\(r_i\)块旧毛巾。
这不难想到,将一天拆开,分成还有的老毛巾和需要的新毛巾。
我们考虑一下,建图过程。
- 从源点向所有老毛巾的点连接容量为 \(r_i\) 费用为0的边,再从所有的新毛巾向汇点连一条容量为\(r_i\)费用为0的边
- 下边三条皆为老毛巾的去处,第二三条为新毛巾的来源
- 对于左边的第\(i\)天的老毛巾,其可以向下一天的老毛巾连一条容量为无穷费用为0的边
- 对于左边的第\(i\)天的老毛巾,其可以向第\(i+m\)天的新毛巾连一条容量为无穷费用为f的边
- 对于左边的第\(i\)天的老毛巾,其可以向第\(i+n\)天的新毛巾连一条容量为无穷费用为s的边
- 对应的新毛巾的最后一条来源,是从源点向每一个新毛巾的连一条容量为无穷费用为p的边
然后跑最小费用最大流即可。
这里着重说一下,就是对于,在最大流的结果下,可能从源点向老毛巾连出的所有边不一定都是满流,可以理解为当天用完后,直接有一部分我们不要了,因为可能直接买效益更高。
AC_code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 4010,M = N*10,INF = 1e8;
int h[N],ne[M],e[M],f[M],w[M],idx;
int q[N],d[N],minT[N],pre[N];
bool st[N];
int r[N];
int n,p,x,xp,y,yp,S,T;
void add(int a,int b,int c,int d)
{
e[idx] = b,ne[idx] = h[a],w[idx] = d,f[idx] = c,h[a] = idx++;
e[idx] = a,ne[idx] = h[b],w[idx] = -d,f[idx] = 0,h[b] = idx++;
}
bool spfa()
{
int hh = 0,tt = 0;
memset(d,0x3f,sizeof d);
memset(minT,0,sizeof minT);
d[S] = 0,q[tt++] = S,minT[S] = INF;
while(hh!=tt)
{
int t = q[hh++];
if(hh==N) hh = 0;
st[t] = 0;
for(int i=h[t];~i;i=ne[i])
{
int j = e[i];
if(f[i]&&d[j]>d[t]+w[i])
{
d[j] = d[t] + w[i];
minT[j] = min(f[i],minT[t]);
pre[j] = i;
if(!st[j])
{
st[j] = 1;
q[tt++] = j;
if(tt==N) tt = 0;
}
}
}
}
return minT[T] > 0;
}
LL EK()
{
LL cost = 0;
while(spfa())
{
int t = minT[T];
cost += 1ll*d[T]*t;
for(int i=T;i!=S;i=e[pre[i]^1])
f[pre[i]] -= t,f[pre[i]^1] += t;
}
return cost;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",r+i);
scanf("%d%d%d%d%d",&p,&x,&xp,&y,&yp);
memset(h,-1,sizeof h);
S = 0,T = 2*n + 1;
for(int i=1;i<=n;i++)
{
add(S,i,r[i],0);
add(i+n,T,r[i],0);
add(S,i+n,INF,p);
if(i+1<=n) add(i,i+1,INF,0);
if(i+x<=n) add(i,i+n+x,INF,xp);
if(i+y<=n) add(i,i+n+y,INF,yp);
}
printf("%lld\n",EK());
return 0;
}

浙公网安备 33010602011771号