关于DP的几种优化

单调队列优化

通常转移条件满足:
d p [ i ] = m a x ( d p [ j ] + a [ j ] ) ( ∣ j − i ∣ < x ) dp[i]=max(dp[j]+a[j])(|j-i|<x) dp[i]=max(dp[j]+a[j])(ji<x)
即转移条件只跟前面的某一项有关,且满足取最值条件。

斜率优化

通常转移条件满足: d p [ i ] = m a x ( d p [ j ] + a [ i ] ∗ a [ j ] ) ( j < i ) dp[i]=max(dp[j]+a[i]*a[j])(j<i) dp[i]=max(dp[j]+a[i]a[j])(j<i)
即存在某一项即有 i i i,又有 j j j
入门·模板
斜率优化比较复杂,涉及到了数形结合。
斜率优化的第一步是找出状态转移
这道题的 n 2 n^2 n2暴力解法很简单,有转移如下
d p [ i ] = m i n 0 < = j < i ( d p [ j ] + ( ∑ k = j + 1 i C k + i − j − 1 − L ) 2 ) dp[i]=min_{0<=j<i}(dp[j]+(\sum_{k=j+1}^{i}C_k+i-j-1-L)^2) dp[i]=min0<=j<i(dp[j]+(k=j+1iCk+ij1L)2)
a i = i + s u m i a_i=i+sum_i ai=i+sumi
a j = j + s u m j + 1 + L a_j=j+sum_j+1+L aj=j+sumj+1+L
d p i = m i n 0 < = j < i ( d p j + ( a i − b j ) 2 ) dp_i=min_{0<=j<i}(dp_j+(a_i-b_j)^2) dpi=min0<=j<i(dpj+(aibj)2)
f ( j ) = d p j + ( a i − b j ) 2 f(j)=dp_j+(a_i-b_j)^2 f(j)=dpj+(aibj)2
斜率优化的第二步要推出状态更优的条件
即判断对于 j < k < i j<k<i j<k<i,找到上面转移满足 f ( j ) > = f ( k ) f(j)>=f(k) f(j)>=f(k)的条件
即k更优于j的条件。
f ( j ) − f ( k ) = d p j − d p k − 2 b j a i + b j b j + 2 b k a i − b k b k > = 0 f(j)-f(k)=dp_j-dp_k-2b_ja_i+b_jb_j+2b_ka_i-b_kb_k>=0 f(j)f(k)=dpjdpk2bjai+bjbj+2bkaibkbk>=0
把有i的部分移到一边:
d p j − d p k + b j b j − b k b k > = 2 a i ( b j − b k ) dp_j-dp_k+b_jb_j-b_kb_k>=2a_i(b_j-b_k) dpjdpk+bjbjbkbk>=2ai(bjbk)
又因为 b j < b k b_j<b_k bj<bk,所以两边同除 b j < b k b_j<b_k bj<bk要取反
d p j + b j b j − d p k − b k b k b j − b k < = 2 a i \frac{dp_j+b_jb_j-dp_k-b_kb_k}{b_j-b_k}<=2a_i bjbkdpj+bjbjdpkbkbk<=2ai
对于 j , k j,k j,k,在二维坐标系上建点: ( b j , d p j + b j b j ) 和 ( b k , d p k + b k b k ) (b_j,dp_j+b_jb_j)和(b_k,dp_k+b_kb_k) (bj,dpj+bjbj)(bk,dpk+bkbk)
那么上述不等式就变成了两点之间的斜率满足的条件
斜率优化的第三步就是要维护点集以及相邻点之间形成的折线了
考虑用单调队列维护这样一个二维坐标系上的点集。
他一定要满足相邻两点之间斜率递增。
在这里插入图片描述
左图维护的即为斜率递增的点集,而右边的则不满足。
那为什么我们一定要维护斜率递增呢?
考虑到我们之前状态更优条件:
即对于 j < k < i j<k<i j<k<i,找到上面转移满足 f ( j ) > = f ( k ) f(j)>=f(k) f(j)>=f(k)的条件

在这里插入图片描述
假设现在 j < k < i j<k<i j<k<i,如图,i和j之间的斜率小于k与j之间的斜率。
再来看斜率状态的转移

while(l<r&&slope(q[l+1],q[l])<2*a[i])l++;
dp[i]=dp[q[l]]+(a[i]-b[q[l]])*(a[i]-b[q[l]]);
while(l<r&&slope(i,q[r])<slope(q[r],q[r-1]))r--;
q[++r]=i;

显然第一行的转移出现了问题。
即存在 s l o p e ( j , k ) > = 2 a [ i ] slope(j,k)>=2a[i] slope(j,k)>=2a[i]但是 s l o p e ( j , i ) < 2 a [ i ] slope(j,i)<2a[i] slope(j,i)<2a[i]的情况,但此时我们选择的却是 k k k点,而不是 i i i点。
所以一定要严格维护斜率递增。
(有理解错误欢迎大佬指出)
附上该题代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 50050
ll sum[MAXN];
ll dp[MAXN];
ll n,x;
ll a[MAXN];
ll b[MAXN];
int q[MAXN];
double slope(int i,int j)
{
    
    
    ll x=dp[i]+b[i]*b[i]-dp[j]-b[j]*b[j];
    ll y=b[i]-b[j];
    return double(x)/y;
}
int main()
{
    
    
    scanf("%lld%lld",&n,&x);
    b[0]=x+1;
    for(int i=1;i<=n;i++)
    {
    
    
        int c;
        scanf("%d",&c);
        sum[i]=sum[i-1]+c;
        a[i]=sum[i]+i;
        b[i]=sum[i]+i+1+x;
    }
    int l=0;
    int r=0;
    for(int i=1;i<=n;i++)
    {
    
    
        while(l<r&&slope(q[l+1],q[l])<2*a[i])l++;
        dp[i]=dp[q[l]]+(a[i]-b[q[l]])*(a[i]-b[q[l]]);
        while(l<r&&slope(i,q[r])<slope(q[r],q[r-1]))r--;
        q[++r]=i;
    }
    cout<<dp[n]<<endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_43353639/article/details/107601592