UESTC 879 (斜率DP)

15 篇文章 0 订阅

转载自 xiper

题意:

链接:这里

解释:

作为斜率DP的入门题,学习了下!
这是一道斜率优化DP的题目.

我们令 f(i,j) 表示将前 i 个人分成 j 份所需的最小费用.

F(i,j) = min{F(u,j1)+(sum[i]sum[u+1])2}

但是这样做的复杂度高达 O(N2M) ,对于本题的规模来讲无法承受,我们考虑

K1<k2<i ,且 k2 k1 更优

有式子

F(k2,j1)+sum(k2)2F(k1,j1)sum(k1+1)2sum(k2)sum(k1+1)<2sum(i)

显然这是一个斜率的式子.

假设现在 K(k1k2)<2sum[i] ,那么 k1 这个点还有价值吗?

我们注意到 sum[i] 是单调不减的,也就是说,对于之后的 i2,i3 …..

K(k1k2)<2sum[i] ,也就是说, k1 完全不可能成为某个点的最优解了,是可以舍弃的.

那么如果现在有 K(k1k2)>=2sum[i] 呢,目前显然是选择 k1 k2 好,我们应不应该舍弃 k2 呢?答案是不应该,因为随着 i 的增大,sum[i] 是不减的(也就是说, sum[i] 可能会增大),此时 K(k1k2)<2sum[i] 是可能成立的.

综上所述,我们维护一个单调队列即可,只不过这个单调队列剔除 / 加入元素的条件都是和与斜率有关,这样我们可以在O(1)的时间内得到某个点的最优决策点,复杂度成功降到了 O(nm) .

代码:
#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值