(初识)斜率dp

关于斜率dp的一点理解

我其实应该先学好几何 orz

前导算法
基础dp
单调队列
数形结合思想

算法干嘛
是动态规划问题中的一种优化方案,
当满足这里写图片描述 时可以利用斜率优化,变化O(n)为O(1)

算法思路
本算法是一种数形结合的优化方法,
可以考虑为 随着i增加 i的待考查区间增大。
如果满足上述斜率等式 ,
易发现,待考察区间有许多无用值
将之去掉,待考察区间 可能变为斜率递增的单调队列
而最优解在维护后的队首

算法阶段
1.遍历i值
2.维护单调队列的队首
2.找到i值对应的最优解(单调队列的队首),为其赋值
3.将新元素加入到单调队列中(维护队尾元素)

个人感觉
是我太笨,还是这个算法太难写
不到十行错了十多次
orz

模板

#include
#include
#include
#include
#include
using namespace std;

long long an[500500];
long long sum[500500];
long long n,m;
long long dp[500500];
long long q[500500];
long long front=1,rear=0;
long long zi(long long a,long long b){
    long long ans=dp[a]-dp[b]+ (sum[a]+sum[b])*(sum[a]-sum[b]);
    return ans;
}
long long mu(long long a,long long b){
    return 2*(sum[a]-sum[b]);
}
    //单调队列的出队 
    while(front+1q[front+1],q[front])<=sum[i]*mu(q[front+1],q[front])){ 
        front++;
    }
    //单调队列的出队 
    dp[i]=dp[q[front]]+(sum[i]-sum[q[front]])*(sum[i]-sum[q[front]])+m;x
    //维护单调队列(斜率递增) 
    while(front+1if( zi(q[rear-1],q[rear-2])*mu(i,q[rear-1])>=zi(i,q[rear-1])*mu(q[rear-1],q[rear-2])){
            rear--;
        }
        else break;

    }
    q[rear++]=i;
}


int main(){
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    while(~scanf("%lld%lld",&n,&m)){
        front=1,rear=1;
        sum[0]=0;
        for(int i=1;i<=n;i++){
            scanf("%lld",&an[i]);
            sum[i]=sum[i-1]+an[i];
        }
        q[rear++]=0;
        dp[0]=0;
        for(long long i=1;i<=n;i++){
            calcul(i);
        }
        printf("%lld\n",dp[n]);
    }
    return 0;
} 

这些题大家可以练习(遇到补充)

  1. HDU - 3507 Print Article 斜率dp
    求打字的最少代价,
    我写的题解

你可能感兴趣的:(ACM,西工大camp前的预习,算法,模板)