题解 CF724E Goods transportation
做法
首先晃眼一看:这不是网络流傻子题吗?
于是立刻建了个图跑最大流:
-
建立源点 $S$,向第 $i$ 个点建立一条容量为 $p_i$ 的边;
-
建立汇点 $T$,第 $i$ 个点向 $T$ 建立一条容量为 $s_i$ 的边;
-
对于所有的 $i<j$,由 $i$ 向 $j$ 建立一条容量为 $c$ 的边。
正当建完图打算交的时候,定睛一看:$n\le10000$,这不明摆着 TLE?
于是就需要另外一个方法:模拟网络流。
首先因为最大流等于最小割,于是原问题就变成了求原网络的最小割。
显然对于一种最小割,每个点要么属于 $S$ 所在的集合,要么属于 $T$ 所在的集合。
并且是由小的连向大的,所以无后效性。
于是令 $f_{i,j}$ 表示前 $i$ 个点中,有 $j$ 个点属于 $S$ 集合的最小割。
于是对于点 $i$,有两种方案:
-
$i$ 属于 $S$ 集合,则割掉 $i$ 连向 $T$ 的边
-
$i$ 属于 $T$ 集合,则割掉 $i$ 连向 $S$ 的边和连向下标小于 $i$ 的边。
故转移方程为:$f_{i,j}=\min(f_{i-1,j}+p_i+j\times c,f_{i-1,j-1}+s_i)$
空间开不下,要使用滚动数组。
代码
#include<cstring>
#include<iostream>
#define int long long
using namespace std;
int f[2][10005],n,a[10005],s[10005],c;
signed main()
{
scanf("%lld%lld",&n,&c);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)scanf("%lld",&s[i]);
for(int i=1;i<=n;i++)
{
memset(f[i&1],0x7f,sizeof(f[i&1]));
f[i&1][0]=f[(i-1)&1][0]+a[i];
for(int j=1;j<=i;j++)f[i&1][j]=min(f[(i-1)&1][j-1]+s[i],f[(i-1)&1][j]+a[i]+j*c);
}
int ans=2e18;
for(int i=0;i<=n;i++)ans=min(ans,f[n&1][i]);
printf("%lld",ans);
}