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\n", dp[n] );
51     }
52 }
View Code

 

posted @ 2015-02-18 18:11  idy002  阅读(279)  评论(0编辑  收藏  举报