关于斜率dp的一点理解
我其实应该先学好几何 orz
算法干嘛
是动态规划问题中的一种优化方案,
当满足 时可以利用斜率优化,变化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;
}
这些题大家可以练习(遇到补充)
- HDU - 3507 Print Article 斜率dp
求打字的最少代价,
我写的题解