转载自 xiper
题意:
链接:这里
解释:
作为斜率DP的入门题,学习了下!
这是一道斜率优化DP的题目.
我们令
f(i,j)
表示将前
i
个人分成
F(i,j) = min{F(u,j−1)+(sum[i]–sum[u+1])2}
但是这样做的复杂度高达 O(N2∗M) ,对于本题的规模来讲无法承受,我们考虑
K1<k2<i ,且 k2 比 k1 更优
有式子
显然这是一个斜率的式子.
假设现在 K(k1–k2)<2∗sum[i] ,那么 k1 这个点还有价值吗?
我们注意到 sum[i] 是单调不减的,也就是说,对于之后的 i2,i3 …..
K(k1–k2)<2∗sum[i] ,也就是说, k1 完全不可能成为某个点的最优解了,是可以舍弃的.
那么如果现在有
K(k1–k2)>=2∗sum[i]
呢,目前显然是选择
k1
比
k2
好,我们应不应该舍弃
k2
呢?答案是不应该,因为随着
i
的增大,
综上所述,我们维护一个单调队列即可,只不过这个单调队列剔除 / 加入元素的条件都是和与斜率有关,这样我们可以在O(1)的时间内得到某个点的最优决策点,复杂度成功降到了 O(n∗m) .
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e4 + 50;
int n,m,h[maxn],f[2][maxn],cur=0,q[maxn];
double slope(int x,int y)
{
if (h[x+1] == h[y+1])
return 1e233;
return (double)(f[cur^1][x] + h[x+1]*h[x+1] - (f[cur^1][y] + h[y+1]*h[y+1])) / (double)(h[x+1] - h[y+1]);
}
int main(int argc,char *argv[])
{
scanf("%d%d",&n,&m);
for(int i = 1 ; i <= n ; ++ i) scanf("%d",&h[i]);sort(h+1,h+1+n);
for(int i = 1; i <= n ; ++ i) f[cur][i] = (h[i]-h[1])*(h[i]-h[1]);
for(int i = 2 ; i <= m ; ++ i)
{
cur ^= 1;
int front = 0 , rear = 0 ;
q[rear++] = 1;
for(int j = 2 ; j <= n ; ++ j)
{
while (rear - front > 1 && slope(q[front],q[front+1]) <= 2*h[j])
front++;
f[cur][j] = f[cur^1][q[front]] + (h[j] - h[q[front]+1])*(h[j] - h[q[front]+1]);
while(rear - front > 1 && slope(q[rear-2],q[rear-1]) >= slope(q[rear-1],j))
rear--;
q[rear++] = j;
}
}
printf("%d\n",f[cur][n]);
return 0;
}