hdu3507

题意: 给n(n<=10^6)个非负数字,放在一个数组num中,再给一个特殊值m。求将这个数组分成任意多个区间,每个区间[a,b]的值定义为( sigma(num[i] | (a<=i<=b)) ) ^ 2 + m.要区间值总和最小,并输出此最小值  (PS:这道题不用考虑暴int问题,当然这是AC以后才发现的)。

解题思路: 定义sum[i]=sigma(num[j] | (1<=j<=i)) (这里假设num数组下标从1开始)

     定义f(j,i) = (sum[i]-sum[j])^2+m (区间[j+1,i]的值)

     定义dp[i]为从1到i这段的题意要求的那个最小值,根据其定义就有dp[i]=min{ dp[j]+f(j,i) | 0<=j<i } (****)。(思考为什么这样定义?)

     另外定义一个仅作标识用的量dp[j,i],表示从j到i这段题意要求的最小区间值和,即dp[1,k]=dp[k]。

裸的枚举当然是不科学的,效率太低。与上一篇博客略有类似,计算dp[i]要用以前的结果,如何对要枚举的状态进行优化呢?

令(k<j<i) ,x>0;

  1. 若有dp[k]+f(k,i)> dp[j]+f(j,i),那么一定有dp[i+x] > dp[k]+f(k+1,x+i); 也就是说 k一定不是对应着i的一个可能最优解。 在求解dp[i+x]时,k点就不用枚举了。
  2. 若有dp[k]+f(k,i)<= dp[j]+f(j,i),那么一定有dp[i+x] > dp[j]+f(j,i+x)。

重新思考一下(****)dp[i]的定义,应用数学归纳法:

dp[1]=f(0,1) 或者dp[1]=f(0,1)+dp[0] (dp[0]=0)

dp[2]=min{dp[1]+f(2,2),dp[0]+f(1,2)} (刚好对应两种区间拆分方式)

假设dp[k]用上式定义也正确,那么

  dp[k+1]=min{ dp[1,j]+dp[j+1,k+1] | (j <= k) },如果dp[j+1,k+1]==f(j,k+1),原式自然成立;

  那即使不成立呢? 也就是说存在一个j<jj<k, 使得dp[j+1,k+1]==f(j,jj)+dp[jj+1,k+1],所以

   dp[1,j]+f(j,jj)+dp[jj+1,k+1]描述的是什么呢?dp[1,j]+f(j,jj)正是dp[jj]的一个可能最优解。dp[k+1]=min{dp[jj]+dp[jj+1,k+1], dp[k+1] },原式成立!

 1 #include <iostream>

 2 #include <cstring>

 3 #include <cstdio>

 4 #include <algorithm>

 5 using namespace std;

 6 const int maxn=500100;

 7 int num[maxn],pl;

 8 int sum[maxn];

 9 int dp[maxn];

10 int que[maxn];

11 int f(int j,int i){

12     return (sum[i]-sum[j])*(sum[i]-sum[j])+pl;

13 }

14 int gety(int j,int k){

15     return dp[j]+sum[j]*sum[j]-dp[k]-sum[k]*sum[k];

16 }

17 int getx(int j,int k){

18     return (sum[j]-sum[k])<<1;

19 }

20 int main()

21 {

22     int n;

23     while(scanf("%d%d",&n,&pl) != EOF){

24         for(int i=1;i<=n;i++)

25             scanf("%d",&num[i]);

26         sum[0]=dp[0]=0;

27         for(int i=1;i<=n;i++)

28             sum[i]=sum[i-1]+num[i];

29         int head=0,tail=0;

30         que[tail++]=0;

31         for(int i=1;i<=n;i++){

32             while(head+1 < tail && gety(que[head+1],que[head])<=getx(que[head+1],que[head])*sum[i])

33                 head++;

34             dp[i]=dp[que[head]]+f(que[head],i);

35             while((head+1 < tail) && gety(i,que[tail-2])*getx(que[tail-1],que[tail-2])<=gety(que[tail-1],que[tail-2])*getx(i,que[tail-2]))

36                 tail--;

37             que[tail++]=i;

38         }

39         printf("%d\n",dp[n]);

40     }

41     return 0;

42 }
View Code

 

你可能感兴趣的:(HDU)