• 一天一道算法题--5.27--思维题


    感谢 微信平台: 一天一道算法题 每天带来很有意义的东西 -----------

    今天它提供的题目 可以参照

      戳我

    题目大意:

    有N个正整数构成的一个序列 给定整数S 求长度最短的连续序列 使他们的和大于或等于S
    输入第一行是 n 和 S (10<n<1000000 S<10^9 )
    第二行输入这n个数 均不能超过10000

    一些解题思路:

    1.

    暴力  0(n^3) 时间复杂度过高 直接略过

    2. 

    我们这边运用的技巧是 前缀和思想 这种方法是 O(n^2)   应该可以接受...
    如果 你以前没有听过 那么现在有必要 好好记住并理解它了 因为它的用处真的很大
    S[i] = S[1] + S[2] + …… + S[I] ;  S[j] - S[i] = S[i] + S[i+1] + …… + S[j] ;

    那么 其实代码就不难实现了 只要对于每个 I 都去遍历 它后面的数组小标 他们的差值 在对value进行比较 如果满足大于等于的条件 就记录下来  这个序列长度自然就是 j-i+1

     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 
     5 const int size = 1000000+10;
     6 int sum[size];
     7 int main()
     8 {
     9     int t;
    10     int i , j;
    11     int n , value , num;
    12     int minLen;
    13     while( ~scanf("%d",&t) )
    14     {
    15         while( t-- )
    16         {
    17             scanf( "%d %d",&n,&value );
    18             sum[0] = 0;//这是个很关键的预处理
    19             minLen = n+1;
    20             for( i = 1 ; i<=n ; i++ )
    21             {
    22                 scanf( "%d",&num );
    23                 sum[i] = sum[i-1] + num;
    24             }
    25             for( i = 1 ; i<=n ; i++ )
    26             {
    27                 for( j = i ; j<=n ; j++ )
    28                 {
    29                     if( sum[j]-sum[i-1] >=value && (j-i+1 < minLen) )
    30                     {
    31                         minLen = j-i+1;
    32                     }
    33                 }
    34             }
    35             printf( "%d
    ", minLen==n+1?0:minLen );
    36         }
    37     }
    38     return 0;
    39 }
    View Code

    一个很悲剧的消息  我们上面的代码 超时了 在zoj的平台上  那么 有必要再进行一下优化了

    这边的code4god 给我们提供了二分的思想

    你可以这样理解:

    我们可以尝试只枚举终点 J , 我们需要找到一个sum[j]-sum[i-1] >= S 并且 i 尽量大 那么序列长度就会变小

    注意到这里的sum数组 是递增的 那么就可以用二分查找     STL直接为我们提供了更好的lower_bound() 函数

    我以前 也听过它 好像没用过它  从这次来看 真的很强大

    至于怎么使用它 你可以自行百度

    好了 现在贴下 这个再次优化后的算法

     1 /*
     2 lower_bound(a,b,c)返回的是从a到b-1区间内第一个大于等于c的指针
     3 正确的做法是lower_bound( sum , sum+i , sum[i]-value+1)
     4 必须找sum[i]-value+1的下界
     5 */
     6  
     7 
     8 
     9 #include <iostream>
    10 #include <cstdio> 
    11 #include <algorithm>
    12 using namespace std;
    13 
    14 const int size = 100000+10;
    15 int sum[size];
    16 
    17 int main()
    18 {
    19     int t;
    20     int n , value , num;
    21     int minLen , pos;
    22     while( ~scanf("%d",&t) )
    23     {
    24         while( t-- )
    25         {
    26             scanf( "%d %d",&n,&value );
    27             sum[0] = 0; 
    28             minLen = n+1;
    29             for( int i = 1 ; i<=n ; i++ )
    30             {
    31                 scanf( "%d",&num );
    32                 sum[i] = sum[i-1] + num;
    33                 pos = lower_bound( sum , sum+i , sum[i]-value+1 ) - sum; 
    34                 if( i>=1 )
    35                 {
    36                     printf( "%d %d
    ",i,pos );
    37                 }
    38                 if( pos>0 )
    39                 {
    40                     minLen = min( minLen , i-pos+1 );
    41                 }
    42             }
    43             printf( "%d
    ",minLen==n+1?0:minLen );
    44         }
    45     }
    46     return 0;
    47 }
    View Code

    貌似 时间一下子 缩短到20ms了吧 没记错的话。。。

    难得一次是在 当天写完的...

     这里 我必须补充一下  如果我的Lower_bound的形参写成 sum[i]-value  在zoj和poj我都测试过 是可以AC的  可能是它们的测试数据不够强大的原因 因为下面这组例子 它输出的就是错的

    5 3

    1 1 1

    这边来分析一下原因-》来源于大神

    现在 我们想找一个k 使得k<i sum[i] - sum[k-1]>=value
    而且 对于所有的p>k sum[i]-sum[p]<value
    我们知道 所有满足sum[i]-sum[k-1]>=value 的都同时满足 sum[k-1]<=sum[i]-value
    即 sum[k-1]<sum[i]-value+1
    所以 我们就可以通过lower_bound(sum , sum+i , sum[i]-value+1)来实现
    我这边再强调一下 lower_bound(a,a+n,value)的查询区间是递增的左闭右开区间  对于这个端点是否取到应该是重点

    刚刚看到了 一个大牛对stl中 lower_bound  和 upper_bound的理解  我就顺便把它的实现   贴过来了

    iterator lower_bound( const key_type &key ): 返回一个迭代器 , 指向键值>= key的第一个元素。
    iterator upper_bound( const key_type &key ):返回一个迭代器 , 指向键值> key的第一个元素。 也可以理解成键值>=KEY的最后一个元素

    博客链接

    根据2分实现的lower_bound

     1 int my_lower_bound(int *array, int size, int key)
     2 {
     3     int first = 0, last = size-1;
     4     int middle, pos=0;       //需要用pos记录第一个大于等于key的元素位置
     5 
     6     while(first < last)
     7     {
     8         middle = (first+last)/2;
     9         if(array[middle] < key){      //若中位数的值小于key的值,我们要在右边子序列中查找,这时候pos可能是右边子序列的第一个
    10             first = middle + 1;
    11             pos = first;
    12         }
    13         else{
    14             last = middle;           //若中位数的值大于等于key,我们要在左边子序列查找,但有可能middle处就是最终位置,所以我们不移动last,
    15             pos = last;              //而是让first不断逼近last。
    16         }
    17     }
    18     return pos;
    19 }
    View Code

    stl中的源码实现lower_bound

     1 //这个算法中,first是最终要返回的位置
     2 int lower_bound(int *array, int size, int key)
     3 {
     4     int first = 0, middle;
     5     int half, len;
     6     len = size;
     7 
     8     while(len > 0) {
     9         half = len >> 1;
    10         middle = first + half;
    11         if(array[middle] < key) {     
    12             first = middle + 1;          
    13             len = len-half-1;       //在右边子序列中查找
    14         }
    15         else
    16             len = half;            //在左边子序列(包含middle)中查找
    17     }
    18     return first;
    19 }
    View Code

    同样 下面就是 它的兄弟 upper_bound

     1 int my_upper_bound(int *array, int size, int key)
     2 {
     3     int first = 0, last = size-1;
     4     int middle, pos = 0;
     5 
     6     while(first < last)
     7     {
     8         middle = (first+last)/2;
     9         if(array[middle] > key){     //当中位数大于key时,last不动,让first不断逼近last
    10             last = middle;
    11             pos = last;
    12         }
    13         else{
    14             first = middle + 1;     //当中位数小于等于key时,将first递增,并记录新的位置
    15             pos = first;
    16         }
    17     }
    18     return pos;
    19 }
    View Code

    stl源码实现upper_bound

     1 int upper_bound(int *array, int size, int key)
     2 {
     3     int first = 0, len = size-1;
     4     int half, middle;
     5 
     6     while(len > 0){
     7         half = len >> 1;
     8         middle = first + half;
     9         if(array[middle] > key)     //中位数大于key,在包含last的左半边序列中查找。
    10             len = half;
    11         else{
    12             first = middle + 1;    //中位数小于等于key,在右半边序列中查找。
    13             len = len - half - 1;
    14         }
    15     }
    16     return first;
    17 }
    View Code

    今天 听到一句话  

    -----------勇敢去爱 不要逃------------

    我想破坏意境的加一句  :

    -----------一切 无所谓---------------

    just follow your heart
  • 相关阅读:
    Ceph对象主本损坏的修复方法
    mds的cpu占用问题分析以及解决办法
    CentOS GRUB损坏修复方法
    掉电后osdmap丢失无法启动osd的解决方案
    怎样禁止Ceph OSD的自动挂载
    Silverlight 预定义颜色速查表
    Silverlight中Image控件Stretch属性的四种值比较
    Silverlight中图片显示
    Silverlight中关于ComboBox的各种使用
    Silverlight样式定义
  • 原文地址:https://www.cnblogs.com/radical/p/3755335.html
Copyright © 2020-2023  润新知