[算法模版]斜率优化
在之前的博客中,有几道斜率优化的例题。但是那些例题都是把“状态”看作“线段”来处理的。最后我发现,还是把状态当成二维平面上的点比较好处理。
这里以昨天模拟赛的一道题作为例题来说一说斜率优化。
本文内容大部分由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#include #include #include #include #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 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 (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]);}