给定N个非负整数,求解至少需要选多少个连续的数,它们的和不小于给定的整数S,特别的,若没有解,则输出0.
这是一个非常经典的应用题,常规思想就是暴力所有不同连续个数的和,满足条件的最少个数为最终答案,不管是实现还是复杂度都是很复杂的。
优化一:因为是连续的数的和,所以可以通过一个额外的数组Sum来存储前缀和,Sum[i]表示第0个数到第i个数的连续的和,那么Sum[i+k]-Sum[i]就表示第i个数起,连续k个数的和,非常方便的获取一段连续数的和,而不用每次都循环累加计算。
优化二:因为答案具有连续性,为0, 1, 2, … , N中的一个,通过二分答案的方式可以进一步降低复杂度。具体步骤为:首先假设答案为N/2,验证是否存在连续N/2个数满足条件,若满足则将答案缩小到0 1 2, … , N/2/2,否则答案在N/2/2+1, … , N这区间里,不断的二分缩小答案区间,最终得到答案。
基于队列的算法则可以将复杂度降到O(N),具体是通过维护一个受条件约束(队列里元素和小于S)的队列Que和一个整数变量tot(队列里元素的和)来实现:
step1:按数组元素顺序依次将元素添加队列Que,整型变量tot依次累加该元素值,直到tot大于等于S时停止入队操作,此时队列长度就是满足条件的备选答案;
step2:移除队首元素,整型变量tot依次减去队首元素,直到tot小于S时停止出队操作;
step3:跳转step1,直到所有数组元素依次被添加到队列,最终答案就是所有备选答案中的最小值。
//
// main.cpp
// step1
//
// Created by ljpc on 2018/9/13.
// Copyright © 2018年 ljpc. All rights reserved.
//
#include
#include
#include
#include
using namespace std;
int main(int argc, const char * argv[]) {
// 请在这里补充代码,完成本关任务
/********* Begin *********/
int n, s, x,b, a[50],sum[100];
scanf("%d %d",&n,&s);
for ( int i = 0 ; i < n ; i++ )
{
scanf("%d",&x);
a[i] = x ;
b = 0;
for ( int j = 0 ; j <= i; j ++ )
{
b+=a[j];
}
sum[i] = b ;
}
//cout << sum[1] << endl << sum[2] <<endl;
int mi = n+1;
for ( int i = 0 ; i < n ; i++ )
{
for ( int k = 0 ; i+k < n ; k++ )
{
//cout << a[i] <<' ' << sum[i+k] << ' ' << sum[i];
int y = a[i] + sum[i+k] - sum[i] ;
/*cout << "i=" << i << "k=" << k << "y=" << y << endl;
cout << a[i] <<' ' << sum[i+k] << ' ' << sum[i]<<endl;
cout << " y = " << y <<endl ;// 从i,i+1 until i+k*/
if ( mi > k+1 && y >= s ) { mi = k+1; /*cout << a[i] <<' ' << sum[i+k] << ' ' << sum[i]<<endl;cout << "i=" << i << "k=" << k << "y=" << y << endl;*/}
}
}
if ( mi != n+1 ) cout << mi << endl;
else cout << 0 <<endl;
/********* End *********/
return 0;
}