(初识)斜率dp

来源:互联网 发布:分析家数据接口 编辑:程序博客网 时间:2024/05/22 17:26

关于斜率dp的一点理解

我其实应该先学好几何 orz

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

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

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

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

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

模板

#include<cstdio>#include<cmath>#include<algorithm>#include<iostream>#include<cstring>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+1<rear&&zi(q[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+1<rear){        if( 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
    求打字的最少代价,
    我写的题解
原创粉丝点击