斜率优化dp - Cats Transport

传送门

Analysis

斜率优化dp好题。
对于第i只猫,显然如果管理员想从出发开始刚好接到它,需要在 t [ i ] = h [ i ] − d i s t ( 1 , i ) t[i]=h[i]−dist(1,i) t[i]=h[i]dist(1,i)的时候出发才行。
这样的话,如果把第l~r只猫分成一组,那么当前分组需要的最小花费是
t [ r ] − t [ l ] + t [ r ] − t [ l + 1 ] + t [ r ] − t [ l + 2 ] + . . . + t [ r ] − t [ r ] = t [ r ] ∗ ( r − l + 1 ) − ( s u m [ r ] − s u m [ l − 1 ] ) t[r]−t[l]+t[r]−t[l+1]+t[r]−t[l+2]+...+t[r]−t[r]=t[r]∗(r−l+1)−(sum[r]−sum[l−1]) t[r]t[l]+t[r]t[l+1]+t[r]t[l+2]+...+t[r]t[r]=t[r](rl+1)(sum[r]sum[l1])
于是就可以推出状态转移方程了:
f [ i ] [ j ] = m i n ( f [ i − 1 ] [ k ] + a [ j ] ∗ ( j − k ) + ( s u m [ j ] − s u m [ k ] ) ) f[i][j]=min(f[i−1][k]+a[j]∗(j−k)+(sum[j]−sum[k])) f[i][j]=min(f[i1][k]+a[j](jk)+(sum[j]sum[k]))
对于两个不同的决策k1,k2。
如果k1转移出的结果比k2优秀,那么:
f [ i − 1 ] [ k 1 ] + a [ j ] ∗ ( j − k 1 ) + ( s u m [ j ] − s u m [ k 1 ] ) &lt; f [ i − 1 ] [ k 2 ] + a [ j ] ∗ ( j − k 2 ) + ( s u m [ j ] − s u m [ k 2 ] ) f[i−1][k1]+a[j]∗(j−k1)+(sum[j]−sum[k1])&lt;f[i−1][k2]+a[j]∗(j−k2)+(sum[j]−sum[k2]) f[i1][k1]+a[j](jk1)+(sum[j]sum[k1])<f[i1][k2]+a[j](jk2)+(sum[j]sum[k2])
=> ( ( f [ i − 1 ] [ k 1 ] − s u m [ k 1 ] ) − ( f [ i − 1 ] [ k 2 ] − s u m [ k 2 ] ) ) &lt; a [ j ] ∗ ( k 1 − k 2 ) ((f[i−1][k1]−sum[k1])−(f[i−1][k2]−sum[k2]))&lt;a[j]∗(k1−k2) ((f[i1][k1]sum[k1])(f[i1][k2]sum[k2]))<a[j](k1k2)
假设 t [ k ] = f [ i − 1 ] [ k ] − s u m [ k ] t[k]=f[i−1][k]−sum[k] t[k]=f[i1][k]sum[k]
=> ( t [ k 1 ] − t [ k 2 ] ) / ( k 1 − k 2 ) &lt; a [ j ] (t[k1]−t[k2])/(k1−k2)&lt;a[j] (t[k1]t[k2])/(k1k2)<a[j]
果断斜率优化了。

<感谢>
注意初值


Code
#include<bits/stdc++.h>
#define ll long long 
#define in read()
#define N 100009
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,m,p;
int t,h;
ll d[N],a[N],s[N],f[105][N];
int l,r,q[N];
inline ll gety(int k,int i,int j){return f[k][i]+s[i]-f[k][j]-s[j];}
inline ll getx(int i,int j){return i-j;}
int main(){
	n=in;m=in;p=in;
	int i,j,k;
	for(i=2;i<=n;++i) {
		int tmp=in;
		d[i]=d[i-1]+tmp;
	}
	for(i=1;i<=m;++i){
		h=in;t=in;
		a[i]=t-d[h];
	}
	sort(a+1,a+m+1);
	for(i=1;i<=m;++i)	s[i]=s[i-1]+a[i];
    fill(f[0]+1,f[0]+m+1,1e18);//初值!!!! 
	ll ans=(1ll<<61);
	for(i=1;i<=p;++i){
		l=r=1;q[1]=0;
		for(j=1;j<=m;++j){
		//	while(l<r&&f[i-1][q[l+1]]+s[q[l+1]]-f[i-1][q[l]]-s[q[l]]<a[j]*(q[l+1]-q[l])) l++;
			while(l<r&&gety(i-1,q[l+1],q[l])<a[j]*getx(q[l+1],q[l]))++l;
			int t=q[l];
		//	printf("%d l=%d\n",t,l);
			f[i][j]=f[i-1][t]+a[j]*(j-t)-s[j]+s[t];
		//	while(l<r&&(f[i-1][q[r]]+s[q[r]]-f[i-1][q[r-1]]-s[q[r-1]])*(j-q[r])>(f[i-1][j]+s[j]-f[i-1][q[r]]-s[q[r]])*(q[r]-q[r-1])) r--;
			while(l<r&&gety(i-1,q[r],q[r-1])*getx(j,q[r])>gety(i-1,j,q[r])*getx(q[r],q[r-1]))--r; 
			q[++r]=j;
		}
		ans=min(ans,f[i][m]);
	}
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值