感谢 微信平台: 一天一道算法题 每天带来很有意义的东西 -----------
今天它提供的题目 可以参照
戳我
题目大意:
有N个正整数构成的一个序列 给定整数S 求长度最短的连续序列 使他们的和大于或等于S
输入第一行是 n 和 S (10
一些解题思路:
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 #include2 #include 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\n", minLen==n+1?0:minLen ); 36 } 37 } 38 return 0; 39 }
一个很悲剧的消息 我们上面的代码 超时了 在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 #include10 #include 11 #include 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\n",i,pos ); 37 } 38 if( pos>0 ) 39 { 40 minLen = min( minLen , i-pos+1 ); 41 } 42 } 43 printf( "%d\n",minLen==n+1?0:minLen ); 44 } 45 } 46 return 0; 47 }
貌似 时间一下子 缩短到20ms了吧 没记错的话。。。
难得一次是在 当天写完的...
这里 我必须补充一下 如果我的Lower_bound的形参写成 sum[i]-value 在zoj和poj我都测试过 是可以AC的 可能是它们的测试数据不够强大的原因 因为下面这组例子 它输出的就是错的
5 3
1 1 1
这边来分析一下原因-》来源于大神
刚刚看到了 一个大牛对stl中 lower_bound 和 upper_bound的理解 我就顺便把它的实现 贴过来了
博客链接
根据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 }
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 }
同样 下面就是 它的兄弟 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 }
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 }
今天 听到一句话
-----------勇敢去爱 不要逃------------
我想破坏意境的加一句 :
-----------一切 无所谓---------------