[算法模版]斜率优化

[算法模版]斜率优化

在之前的博客中,有几道斜率优化的例题。但是那些例题都是把“状态”看作“线段”来处理的。最后我发现,还是把状态当成二维平面上的点比较好处理。

这里以昨天模拟赛的一道题作为例题来说一说斜率优化。

ZROI-#885-猫

本文内容大部分由Harry_bh撰写,特此鸣谢。


\(Dp_{i,j}=Dp_{i-1,k}\)

\(dp_i=dp'_j+val(i-j)-(s_i-s_j)\)

假设 \(j\) 是最优决策点

假设 \(j<k\)

对于任意的 \(k\) 都满足

\[dp_j+val(i-j)-(s_i-s_j)\leq dp_k+val(i-k)-(s_i-s_k)\\ dp_j-dp_k+s_j-s_k\leq val(j-k)\\ \cfrac{dp_j-dp_k+s_j-s_k}{j-k}\geq val\\ \]

这个东西他的意义就是 两个点 \((j,dp_j+s_j)\),\((k,dp_k+s_k)\) 所形成的直线的斜率

所以如果 \(j\)\(k\) 更优,那么这个斜率就满足。。。。

你维护一个队列,那么我们要使得队首元素就是我们想要的最优决策点,因为队首元素成立的条件是\(第一个点和第二个点的斜率\geq val\)。而\(val\)是单调递增的,所以我门需要让队列中每个点和上一个点的斜率是单调递增的(即下凸壳)。

我们在向队尾插入新的元素时,需要判断,如果\(tail,tail-1\)的斜率大于\(插入元素,tail-1\)的斜率时,就需要把\(tail\)删掉。(具体的意思就是\(tail\)的状态并没有优于插入元素,而在这个队列中的性质是每个队中状态要优于他后面的所有状态)

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
#define maxn (int)(1e5+100)
ll dp[1005][50020],dis[maxn],tim[50020],n,m,p,pre[maxn],h[maxn],t[maxn],queue[maxn];
using namespace std;
int main() {
    scanf("%lld%lld%lld",&n,&m,&p);
    for(int i=2;i<=n;i++){scanf("%lld",&dis[i]);dis[i]+=dis[i-1];}
    for(int i=1;i<=m;i++){scanf("%lld%lld",&h[i],&t[i]);tim[i]=t[i]-dis[h[i]];}
    sort(tim+1,tim+1+m);for(int i=1;i<=m;i++)pre[i]=tim[i]+pre[i-1];
    memset(dp,0x3f,sizeof(dp));dp[0][0]=0;
    for(int i=1;i<=p;i++) {
        ll head=1,tail=0;
        for(int j=0;j<=m;j++) {
            while(head<tail&&(dp[i-1][queue[head]]+pre[queue[head]]-dp[i-1][queue[head+1]]-pre[queue[head+1]])>tim[j]*(queue[head]-queue[head+1]))head++;
            dp[i][j]=dp[i-1][queue[head]]+tim[j]*(j-queue[head])-(pre[j]-pre[queue[head]]);
            while(head<tail&&(dp[i-1][queue[tail-1]]+pre[queue[tail-1]]-dp[i-1][queue[tail]]-pre[queue[tail]])*(queue[tail-1]-j)>(dp[i-1][queue[tail-1]]+pre[queue[tail-1]]-dp[i-1][j]-pre[j])*(queue[tail-1]-queue[tail]))tail--;
            queue[++tail]=j;
        }
    }
    printf("%lld",dp[p][m]);
}
posted @ 2019-08-08 08:32  GavinZheng  阅读(154)  评论(0编辑  收藏