参考:传送门
在做dp的时候一个状态i需要从0~i-1的状态中选择最优状态来进行转移,而单纯的这样转移,时间复杂度是O(n^2)的,使用斜率优化dp可以将时间复杂度降低到O(n)或者O(nlogn)。
如果状态i从一个状态转移j比从另一个状态k转移好,那么他们之间肯定会存在一个包含i,j,k的不等式,如果这个不等式能够化成左边只和j,k有关的斜率,右边只和i又管,那么就是j,k之间的某种斜率满足关于i的一个不等式就证明j状态转移比k状态好。这样,我们用队列维护一个凸包,就可以。如果不等式右边根据i状态的递增或者递减的那么直接改变队头,O(1)就可以,如果不是递增或递减,那么我们不改变队头,在队头和队尾之间二分斜率,满足那个斜率的后一个点就是最好的转移点,O(logn)。
例题:hdu3507
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+100;
ll c[maxn];
ll dp[maxn];
ll que[maxn];
ll getX(int a,int b)
{
return c[b] -c[a];
}
ll getY(int a,int b)
{
return ( dp[b] + c[b] * c[b] ) - ( dp[a] + c[a] * c[a] );
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
c[0] = 0;
dp[0] = 0;
int head = 0,tail = 1;
que[0] = 0;
for(int i = 1;i<=n;i++)
{
scanf("%lld",&c[i]);
c[i] += c[i-1];
while(head<tail-1&&getY(que[head],que[head+1])<getX(que[head],que[head+1])*2*c[i])++head;
dp[i] = dp[que[head]] + (c[i] - c[que[head]]) * (c[i] - c[que[head]]) + m;
while(head<tail-1&&getY(que[tail-2],que[tail-1]) * getX(que[tail-1],i)>=getY(que[tail-1],i) * getX(que[tail-2],que[tail-1]))--tail;
que[tail++] = i;
}
printf("%lld\n",dp[n]);
}
}
例题:牛客
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
ll a[maxn],b[maxn],que[maxn];
ll getX(int k,int j)
{
return j - k;
}
ll getY(int k,int j)
{
return (j * a[j] - b[j]) - (k * a[k] - b[k]);
}
bool check(int x2,int x1,ll t)
{
return (x2 * a[x2] - b[x2]) - (x1 * a[x1] - b[x1] ) > t * (x2 - x1);
}
int main()
{
int n;
scanf("%d",&n);
a[0] = b[0] = 0;
int head = 0,tail = 1;
que[0] = 0;
ll ans = -1e18;
for(int i = 1;i<=n;++i)
{
scanf("%lld",&a[i]);
b[i] = i * a[i] + b[i-1];
a[i] += a[i - 1];
int l = 1,r = tail-1,mid,tmpAns = 0;
while(l<=r)
{
mid = (l+r)>>1;
if(check(que[mid],que[mid-1],a[i]))
{
tmpAns = mid;
l = mid + 1;
}
else r = mid - 1;
}
ans = max(ans,(b[i] - b[que[tmpAns]]) - (a[i] - a[que[tmpAns]])*que[tmpAns]);
while(head+1<tail && getX(que[tail-2],que[tail-1])*getY(que[tail-1],i)>=getX(que[tail-1],i)*getY(que[tail-2],que[tail-1]))--tail;
que[tail++] = i;
}
printf("%lld\n",ans);
}