天天看點

#斜率優化,單調隊列,動态規劃#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);
}