天天看点

#斜率优化,单调队列,动态规划#loj 10187 洛谷 CF311B Cats Transport题目分析代码

题目

有 M M M只猫,雇了 P P P位饲养员。路边有 N N N座山,从 1 1 1到 N N N编号。第 i i i座山与第 i − 1 i−1 i−1座山之间的距离是 D i D_i Di​​ 。饲养员都住在 1 1 1号山上。第 i i i只猫去 H i H_i Hi​号山玩,玩到时刻 T i T_i Ti​停止,然后在原地等饲养员来接。饲养员们必须回收所有的猫。每个饲养员沿着路从 1 1 1号山走到 N N N号山,把各座山上已经在等待的猫全部接走。饲养员在路上行走需要时间,速度为 1 1 1米每单位时间。饲养员在每座山上接猫的时间可以忽略,可以携带的猫的数量为无穷大。

你的任务是规划每个饲养员从 1 1 1号山出发的时间,使得所有猫等待时间的总和尽量小。饲养员出发的时间可以为负。

分析

设 A i = T i − ∑ j = 1 d i A_i=T_i-\sum_{j=1}^{d_i} Ai​=Ti​−∑j=1di​​

这道题朴素的动态规划, f [ i ] [ j ] = m i n { f [ i − 1 ] [ k ] + A [ i ] ∗ ( j − k ) − ( ∑ p = k + 1 j a [ p ] ) } f[i][j]=min\{f[i-1][k]+A[i]*(j-k)-(\sum_{p=k+1}^ja[p])\} f[i][j]=min{f[i−1][k]+A[i]∗(j−k)−(∑p=k+1j​a[p])}

所以可以发现这是一个斜率优化,发现这个,还可以发现求的是下凸壳

代码

#include <cstdio>
#include <algorithm>
typedef long long ll; ll f[2][100001],a[100001];
int n,m,p,d[100001],q[100001];
ll in(){
    ll ans=0; int f=1; char c=getchar();
    while ((c<48||c>57)&&c!='-') c=getchar();
    if (c=='-') c=getchar(),f=-f;
    while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
    return ans*f;
}
int main(){
    n=in(); m=in(); p=in(); ll ans=1ll<<62;
    for (register int i=2;i<=n;i++) d[i]=d[i-1]+in();
    for (register int i=1,x;i<=m;i++) x=in(),a[i]=in()-d[x];
    std::stable_sort(a+1,a+1+m); bool x=0;
    for (register int i=2;i<=m;i++) a[i]+=a[i-1];
    std::fill(f[x]+1,f[x]+1+m,1ll<<62);
    for (register int i=1;i<=p;i++){
    	int l=1,r=1; q[1]=0; x^=1;
    	for (int j=1;j<=m;j++){
    		while (l<r&&f[x^1][q[l+1]]+a[q[l+1]]-a[q[l]]-f[x^1][q[l]]<(a[j]-a[j-1])*(q[l+1]-q[l])) l++;//队头没有用的需要出队
    		f[x][j]=f[x^1][q[l]]+(a[j]-a[j-1])*(j-q[l])-(a[j]-a[q[l]]);//动态规划,当然用了滚动数组
    		while (l<r&&(f[x^1][q[r]]+a[q[r]]-f[x^1][q[r-1]]-a[q[r-1]])*(j-q[r])>(f[x^1][j]+a[j]-a[q[r]]-f[x^1][q[r]])*(q[r]-q[r-1])) r--;//队尾不优的出队
			q[++r]=j;//入队
    	}
    	ans=std::min(ans,f[x][m]);
    }
    return !printf("%lld",ans);
}