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 }

 

 

 

你可能感兴趣的:(poj)