斜率优化 DP
Posted taylorswift13
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了斜率优化 DP相关的知识,希望对你有一定的参考价值。
CF311B Cats Transport
暑假到现在终于过了这道题??.
首先计算出(A[]),(A[i])表示如果有一个饲养员在(A[i])时刻出发,那么刚好使第(i)只猫无需等待地被接走.
发现(A[])与猫的编号无关,对其排序并求前缀和(S[]).
设(F[i,j])表示前(i)个饲养员接走了前(j)只猫的最小等待时间和.
朴素算法有:
[F[i,j]=min_{k<j}(F[i-1,k]+A[j] imes (j-k)-(S[j]-S[i]))]
分别枚举(i,j,k),时间复杂度(O(n^3)).
考虑对最内层DP进行斜率优化,此时把与(i,j)相关的式子视为常量,把(k)视为变量,去掉(min)函数,把状态转移方程变形,有:
[F[i-1,k]+S[k]=A[j] imes k+F[i,j]-A[j] imes j+S[j] ]
令:
[y=F[i-1,k]+S[k]]
[x=k]
[b=F[i,j]-A[j] imes j+S[j]]
那么有(y=A[j] imes x+b)
容易发现这是一个一次函数,一但(x)确定,则可以在坐标轴中画出对应的点.换句话说,每一个(k)对应的是一个点.这些点的横坐标是单增的,因为(j)是从(1...m)的.
我们想让(F[i,j])最小,就是让(b)最小,也就是让纵截距最小.而斜率(A[j])是确定的显然在所有点中,只有下凸壳上的点可能使纵截距出现最小值.所以我们维护下凸壳.
下凸壳上的哪个点会使纵截距出现最小值呢?如果记(slope(i,j))表示点(i)与点(j)连线的斜率,那么:
[slope(i-1,i)<=A[j],slope(i,i+1)>=A[j](*)]
的点会使纵截距出现最小值.
本题中,(A[j])单增,所以可以用单调队列维护这个下凸壳.队首出队的条件是:队首两点间斜率(<A[j]),因为(A[j])单增,而这两点斜率又(<A[j]),那么队首的点无论如何不可能成为满足(*)式的最优情况.
从队尾入队的条件是:能形成下凸壳.
#include<bits/stdc++.h>
const int SIZE=100005;
int In()
{
char ch=getchar();
int x=0;
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x;
}
int n,m,p,h,Tim,sum_D[SIZE];
int A[SIZE];
long long DP[105][SIZE],sum_A[SIZE];
int q[SIZE],L,R;
int main()
{
scanf("%d%d%d",&n,&m,&p);
for(int i=2;i<=n;i++)
sum_D[i]=sum_D[i-1]+In();
for(int i=1;i<=m;i++)
{
h=In();Tim=In();
A[i]=Tim-sum_D[h];
}
std::sort(A+1,A+1+m);
for(int i=1;i<=m;i++)sum_A[i]=sum_A[i-1]+A[i];
memset(DP,0x3F,sizeof(DP));
DP[0][0]=0;
for(int i=1;i<=p;i++)
{
L=R=1;
q[1]=0;
#define G(x) (DP[i-1][x]+sum_A[x])
#define S(A,B) (double)(G(B)-G(A))/(double)(B-A)
for(int k=1;k<=m;k++)
{
while( L<R && S(q[L],q[L+1]) <= 1.0*A[k] )L++;
DP[i][k]=DP[i-1][q[L]]+1LL*A[k]*(k-q[L])-(sum_A[k]-sum_A[q[L]]);
while( L<R && S(q[R-1],q[R]) >= S(q[R],k) )R--;
q[++R]=k;
}
}
std::cout<<DP[p][m];
return 0;
}
以上是关于斜率优化 DP的主要内容,如果未能解决你的问题,请参考以下文章