zoukankan      html  css  js  c++  java
  • hdu 3507 斜率优化

    我的第一道斜率优化。

    就这道题而言,写出原始的方程:

      dp[i] = min{ dp[j] + (sum[i]-sum[j])+ M | j in [0,i) }

    O(n^2)的复杂度肯定超时,要么优化转移,要么重写方程。

    斜率优化的思想就是减少不必要的枚举(即不枚举肯定不会成为决策点的j)。

    我们考虑两个位置p<q<i

    “选择q比选择p优” 当且仅当 dp[q]+(sum[i]-sum[q])2+M < dp[p]+(sum[i]-sum[p])2+M

    化简右边即:

    [ (dp[q]+sum2[q])-(dp[p]+sum2[p]) ] / ( sum[q]-sum[p] ) < sum[i]*2

    该式可以看成两个点连线的斜率:( sum[q], dp[q]+sum2[q] ) 与 ( sum[p], dp[p]+sum2[p] ) 两点。

    文字语言就是:“将每个决策位置看成一个二维坐标系下的点,对于两个决策点,后者比前者优 当且仅当 两点连线的斜率小于sum[i]*2”

    这样怎么减少不必要的枚举呢?

    可以发现,所有决策点一定是单调不下降的(题中可能出现权值为0,此时有可能出现斜率为正无穷,若M=0,还有可能出现重点,所以计算斜率不要用除法)

    上面的B点一定是不会成为最优决策点的,反证法:

    如果B成为最优决策点,那么

    2*sum[i]>kab 且 2*sum[i]<kbc

    而显然kab > kbc ,这样就推出了2*sum[i]>kab >kbc >2*sum[i],矛盾。

    故B不可能成为最优决策点,同理,D也不行,删掉这些点后,我们剩下的图形就是一个下凸的图形了:

     

    我们维护这样一个下凸的图形到队列中:

    当要查找i位置的最优决策点时,一直删除队首的点,直到队中的第一条直线的斜率大于2*sum[i]或队中只有一个点,此时队首元素就是最优决策点。

    计算完i位置后,要将i位置对应的点加入到队列中,此时会删除一些对尾的点,以保持队中点的下凸性(注意处理重合的点)。

    这样,我们就利用斜率优化掉了很多不必要的枚举,将时间复杂度从O(n^2)降到了O(n)。

      

     1 #include <cstdio>
     2 #define ln(A,B) ((B)-(A))
     3 #define maxn 500010
     4 
     5 typedef long long lng;
     6 
     7 struct Vector {
     8     lng x, y;
     9     int id;
    10     Vector(){}
    11     Vector( lng x, lng y, int id ) : x(x), y(y), id(id) {}
    12     Vector operator-( const Vector & b ) const { return Vector(x-b.x,y-b.y,0); }
    13     lng operator&( const Vector & b ) const {
    14         return x*b.y-y*b.x;
    15     }
    16 };
    17 typedef Vector Point;
    18 
    19 int n, m;
    20 int cost[maxn];
    21 lng sum[maxn];
    22 lng dp[maxn];
    23 
    24 int beg, end;
    25 Point qu[maxn];
    26 
    27 int main() {
    28     while( 1 ) {
    29         if( scanf( "%d%d", &n, &m )!=2 ) return 0;
    30 
    31         sum[0] = 0;
    32         for( int i=1; i<=n; i++ ) {
    33             scanf( "%d", cost+i );
    34             sum[i] = sum[i-1]+cost[i];
    35         }
    36 
    37         dp[0] = 0;
    38         qu[beg=end=0] = Point( 0, 0, 0 );
    39 
    40         for( int i=1; i<=n; i++ ) {
    41             while( end>beg && qu[beg+1].y-qu[beg].y<=(qu[beg+1].x-qu[beg].x)*2*sum[i] )
    42                 beg++;
    43             int j = qu[beg].id;
    44             dp[i] = dp[j]+(sum[i]-sum[j])*(sum[i]-sum[j])+m;
    45             Point npt = Point( sum[i], dp[i]+sum[i]*sum[i], i );
    46             while( end>beg && (ln(qu[end-1],qu[end])&ln(qu[end-1],npt))<=0 )
    47                 end--;
    48             qu[++end] = npt;
    49         }
    50         printf( "%lld
    ", dp[n] );
    51     }
    52 }
    View Code
  • 相关阅读:
    神经网络学习之----单层感知器
    神经网络学习之----神经网络发展史
    神经网络学习之----神经网络概述
    C语言几种常见的字符串输入
    基于单链表实现集合的交集、并集、差集的运算
    关于单链表的一些基本操作
    输入20个整数存放到一个单向链表中,并顺序逆序输出
    《你的灯亮着吗》阅读笔记
    场景调研
    站立会议总结09
  • 原文地址:https://www.cnblogs.com/idy002/p/4295868.html
Copyright © 2011-2022 走看看