zoukankan      html  css  js  c++  java
  • 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
    ",dp[n]);
    40     }
    41     return 0;
    42 }
    View Code
  • 相关阅读:
    Vue生命周期,及父子组件生命周期顺序
    使用jquery制作可视化的组织结构
    用Moon.Orm来做分页数据显示
    bash脚本之代码统计
    CSS选择符总结
    css选择符归类
    APP测试与WEB测试的区别
    使用Jmeter 对APP进行压力测试
    Python基础之数据类型
    App测试要点以及Bug分类
  • 原文地址:https://www.cnblogs.com/karlvin/p/3292479.html
Copyright © 2011-2022 走看看