CF724E Goods transportation 题解

题目传送门

有显然的网络流模型:选定源点与汇点,从源点向每一个点 \(i\) 连一条流量为 \(p_i\) 的边,从每个点 \(i\) 向汇点连一条流量为 \(s_i\) 的边,每一个点连向编号比它大的节点,流量为 \(c\),之后求最大流即可。

可惜的是由于数据范围过大这样的最大流解法会被卡掉。想到最大流可以转化为最小割,所以转化为 DP 解决。设 \(f[i][j]\) 为前 \(i\) 个节点中有 \(j\)​​​ 个与源点相连。

如果点 \(i\) 与源点相连,要割掉该点到汇点的边;如果点 \(i\)​ 与汇点相连,不仅要割掉源点到该点的边,还要把之前向外输出流量的点到该点的边割掉。

因此有状态转移方程:

\[f_{i,j}=\max(f_{i-1,j-1}+s_i,f_{i-1,j}+p_i+c\times j) \]

空间复杂度过高,在实现中应当使用滚动数组。

View code:

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ri register int
#define il inline

const ll INF=0x7fffffffffff,N=1e5+10;
ll n,c,ans=INF;
ll p[N],s[N];
ll f[N][2];

il ll read(){
    ll x=0,y=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
            y=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
    return x*y;
}

signed main(){
    n=read(),c=read();
    for(ri i=1;i<=n;i++)
        p[i]=read();
	for(ri i=1;i<=n;i++)
        s[i]=read();
    for(ri i=1;i<=n;i++)
        f[i][1]=INF;
    for(ri i=1;i<=n;i++){
        f[0][0]=f[0][1]+p[i];
        for(ri j=1;j<=i;j++)
            f[j][0]=min(f[j-1][1]+s[i],f[j][1]+p[i]+c*j);
        for(ri j=0;j<=i;j++)
            f[j][1]=f[j][0];
    }
    for(ri i=0;i<=n;i++)
        ans=min(ans,f[i][1]);
    printf("%lld",ans);
    return 0;
}
posted @ 2021-08-03 16:46  BFNewdawn  阅读(48)  评论(0编辑  收藏  举报