POJ 3709 斜率优化DP

此题有是方程好写,优化很难(对于神犇们,简直太水了)的一道题

建议做这道题之前先看这道题:http://www.lydsy.com/JudgeOnline/problem.php?id=1010

题意:

将一个升序的,有N个元素的序列,分组。要求每组的元素不少于K个,计算出组内各元素与最小元素的之差的和,将每组的这个值加起来,其和要最小。

思路:

由以上可得DP方程:

dp[i]=MIN(dp[j]+sum[i]-sum[j]-(i-j)*arr[j+1]); j<i-k+1

 

开始斜率优化(不考虑每组不少于K个元素):

1.证明较优决策点对后续状态影响的持续性

  证明很简单,不证了,有兴趣的话,参考上一篇文章

 

2.求斜率方程:一般化为左边是JK,右边是I的形式

  假设J<K,且在K点的决策比J好,则有

 dp[j]+sum[i]-sum[j]-(i-j)*arr[j+1]>= dp[k]+sum[i]-sum[k]-(i-k)*arr[k+1]

化简得:

  dp[j]-dp[k]-sum[j]+sum[k]+j*arr[j+1]-k*arr[k+1]>=i* (arr[j+1]-arr[k+1])

 

G(k,j)= dp[j]-dp[k]-sum[j]+sum[k]+j*arr[j+1]-k*arr[k+1]

   S(k,j)= arr[j+1]-arr[k+1]

 

则上式化为G(k,j)>=i*S(k,j)

G(k,j)/S(k,j)<=i 记住变号,因为S(k,j)<0

X(k,j)= G(k,j)/S(k,j)

所以斜率方程:X(k,j)<=i

 

3.规定队列的维护规则

队首维护:

  假设A,B(A<B)是队首元素,X(B,A)<=i,BA,删除A,否则不需维护.

 

队尾维护:

    假设A,B,C(A<B<C)是队尾元素

a.X(B,A)<=i,X(C,B)<=i,CB,BA

b.X(B,A)<=i,X(C,B)>i,BC,BA,B为极大值

c.X(B,A)>i,AB

 

a,c情况直接删掉B,b情况保留.b情况可改为X(B,A)<X(C,B)

 

好,以下考虑每组不少于K个元素这个限制。

要解决这个限制,只需延迟加入的时机即可。

若延迟K-1个回合加入,有可能使前一组的个数少于K个。

若延迟2*k-1个回合加入,则不会出现这情况。但此时加入的数应是i-k+1(假设是第I回合)

第一次做斜率优化DP,照着题解写的,以上内容转自:http://blog.sina.com.cn/s/blog_5f5353cc0100jxxo.html

代码按照题解思路写的:

DP真的好神奇!~

View Code
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <iostream>
 5 
 6 #define N 505000 
 7 
 8 using namespace std;
 9 
10 int n,k,tt;
11 __int64 dp[N],sum[N],a[N],q[N];
12 
13 void read()
14 {
15     scanf("%d%d",&n,&k);
16     for(int i=1;i<=n;i++)
17     {
18         scanf("%I64d",&a[i]);
19         sum[i]=sum[i-1]+a[i];
20     }
21 }
22 
23 __int64 G(int y,int x)
24 {
25     return dp[x]-dp[y]-sum[x]+sum[y]+x*a[x+1]-y*a[y+1];
26 }
27 
28 __int64 S(int y,int x)
29 {
30     return a[x+1]-a[y+1];
31 }
32 
33 void go()
34 {
35     dp[0]=0;
36     int h=1,t=1;
37     q[t++]=0;
38     for(int i=1,x,y,z;i<=n;i++)
39     {
40         while(h<t-1&&G(q[h+1],q[h])>=i*S(q[h+1],q[h])) h++;//把不可能成为最优值的出队 
41         
42         dp[i]=dp[q[h]]+sum[i]-sum[q[h]]-(i-q[h])*a[q[h]+1];
43         
44         if(i>=2*k-1) q[t++]=i-k+1;//延迟更新 
45         
46         for(int j=t-2;j-1>=h;j--)
47         {
48             x=q[j-1]; y=q[j]; z=q[j+1];
49             if(G(y,x)*S(z,y)>=G(z,y)*S(y,x)) q[j]=q[--t];
50             else break;
51         }
52     }
53     printf("%I64d\n",dp[n]);
54 }
55 
56 int main()
57 {
58     scanf("%d",&tt);
59     while(tt--)
60     {
61         read();
62         go();
63     }
64     return 0;
65 }

 

没有人能阻止我前进的步伐,除了我自己!
原文地址:https://www.cnblogs.com/proverbs/p/2713104.html