zoukankan      html  css  js  c++  java
  • 斜率优化

    BZOJ . 1010

    题意:

            这道题的意思就是说有n个数,分成连续的若干段,每段(假设从第j个到第i个组成一段)的分数为 (X-L)^2,X为j-i+Sigma(Ck) ,i<=k<=j,其中L是一个常量。目标:各段分数的总和最小。

    题解:

           首先这道题是一道dp题,我们设dp[ i ]表示从0 到 i 这一段的最小花费,再用一个sum[ i ]来记录一个前缀和,所以我们可以写出状态转移方程:

                            dp[ i ] = min( dp[ j ] + ( s[ i ] - s[ j ] + i - ( j  + 1 ) - L ) ^ 2;

           但是我们发现这样的话,我们要枚举i 从 1 到 n,枚举j 从 1 到 i,时间复杂度为O ( n ^2 ),而n的范围是50000,所以这样是肯定过不了的,所以程序需要优化。

           我们假设存在这样的k,使得0<j<k<=i,利用四边形不等式有关知识,我们可以发现这道问题具有决策单调性,所以若存在一个k满足比j的方案更优,则j不会用来更新dp[ i ],所以我们可以得到:

    dp[ j ] + ( s[ i ] + s[ j ] + i - ( j + 1 ) - L ) ^ 2 >= dp[ k ] + ( s[ i ] + s[ k ] + i - ( k + 1 ) - L ) ^ 2

    令s[ i ] + i = sum[ i ], L + 1 = c;

           经过整理我们得到:

    dp[ k ] + ( sum[ k ] + c ) ^ 2 - (dp[ j ] + ( sum[ j ] + c ) ^ 2 ) <=2 * s[ i ] * ( s[ k ] - s[ j ] ) 

           我们将 ( s[ k ] - s[ j ] )  * 2移到左边就可以得到一个类似于数学中斜率求法的东西,然后我们在通过这个不等式在一个队列中来维护一个下凸壳。

           队列中存放有可能更新i的点,我们每次进队前先找到用哪一个点来更新当前的i,具体做法是判断s[ i ]和队头元素和队头后一个元素的斜率( 就是之前得到的不等式将( s[ k ] - s[ j ] )  * 2移到左边得到的式子 ),若s[ i ]小于当前斜率,那么用当前队头元素更新dp[ i ],否则头指针指向下一元素,用更新之后的头指针来更新dp[ i ],更新完dp[ i ]后,再来维护队尾,通过下凸壳来维护,这样优化之后就可以只需要跑一边每个点,就可以得到答案,时间复杂度降为O( n ),最后答案就是输出dp[ n ]。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int N = 500001;
    long long q[N],dp[N],s[N],n,l,c;
    
    long long sq(long long a){
      return a * a;
    }
    
    long long omg(int a, int b){
      return dp[b] + sq(s[b] + c) - dp[a] - sq(s[a] + c);//double可能由精度限制,所以能用整型就用整型
    }
    
    int main(){
      scanf("%lld%lld",&n,&l);
      for (int i = 1; i <= n; i++){
          scanf("%lld",&s[i]);
          s[i] += s[i - 1];
      }
      for (int i = 1; i <= n; i++) s[i] += i;
      c = l + 1;
      int head = 1, tail = 1;
      q[tail] = 0;
      for (int i = 1; i <= n; i++){
          while (head != tail && omg(q[head],q[head + 1]) <= 2 * s[i] * (s[q[head + 1]] - s[q[head]])) head++;//先找到用哪一个元素更新当前dp[ i ];int t = q[head];
          dp[i] = dp[t] + sq(s[i] - s[t] - c);//更新dp[ i ];
          while (head != tail && omg(q[tail],i) * 2 * (s[q[tail]] - s[q[tail - 1]]) < omg(q[tail - 1],q[tail]) * 2 * (s[i] - s[q[tail]])) tail--;//维护队尾
          q[++tail] = i;//易错 : 记得把当前元素进队
      }
      printf("%lld
    ",dp[n]);
      return 0;
    }

     

  • 相关阅读:
    Web中的通配符
    EJB版本
    package-info.java
    dynamic web module version
    深入解析和反思携程宕机事件
    Ubuntu 环境下使用cronolog 切割Tomcat日志
    ubuntu环境下cassandra安装配置
    zabbix自定义触发器语法
    zabbix系统邮件告警Python脚本
    Ubuntu14.04下zabbix2.4.5 源码编译安装
  • 原文地址:https://www.cnblogs.com/ganster/p/8487849.html
Copyright © 2011-2022 走看看