输入一个N,和一个S,第二行输入N的整数,要求的是由这N个数组成的数列中,一个连续的子数列的和大于或等于S时,这个子数列的长度最小是多少?
解题报告:算法竞赛入门经典上的题,一开始用自己的思路去做,按照我的思路时间更短,但一直WA,不知道为什么,我的思路就是设置两个指针,一个指向子数列的开头的前一个,另一个指向子数列的最后一个,然后在循环条件j <= N的条件下,另外设一个sum 保存当前的子数列的和,然后判断如果当前子数列的和比S小,则将尾指针向后移动一位并将sum的值相应的增加,如果判断出来当前子数列的和比S大,则判断当前子数列的长度并且比较是否是最短的,然后将子数列的前指针向后移动一位,同时将sum的值减去相应的值,这样最后得出的长度最小的子数列的长度便是我们要的答案。但是不知道是有漏洞还是算法本来就不可行,交上去一直WA,麻烦如果知道的童鞋告诉我一下。然后看了大白书之后,有一种新的算法,我觉得没我的快,但结果是对的,没办法。他的做法是先预处理,求出从第一个数到第 i 个数的和并保存起来,然后只要枚举尾节点,然后内层再嵌套一个循环判断满足
到这个尾节点的和大于等于S的前头节点最大的也就是长度最小的子数列。
下面是我的错误代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 6 const int maxn = 100000; 7 int A[maxn+5]; 8 9 int main() 10 { 11 int N,S; 12 while(scanf("%d%d",&N,&S)!=EOF) 13 { 14 A[0] = 0; 15 for(int i = 1;i <= N;++i) 16 scanf("%d",&A[i]); 17 int i = 0,j = 0,ans = 0x7ffffff,sum = 0; 18 while(j <= N) 19 { 20 if(sum < S) 21 sum += A[++j]; //当前子数列的和小于S时,尾节点向后移动,并加上后一个节点的值 22 else 23 { 24 ans = min(ans,j - i); //当前子数列的和大于等于S时,先判断是不是长度最小的,然后再把前指针往后移动 25 i++; 26 sum -= A[i]; 27 } 28 } 29 printf(ans>100000? "0":"%d ",ans); 30 } 31 return 0; 32 } 33
下面是AC的代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 6 const int maxn = 100000; 7 int A[maxn+5]; 8 9 int main() 10 { 11 int N,S; 12 while(scanf("%d%d",&N,&S)!=EOF) 13 { 14 A[0] = 0; 15 for(int i = 1;i <= N;++i) 16 { 17 scanf("%d",&A[i]); 18 A[i] = A[i-1] + A[i]; 19 } 20 int ans = 0x7ffffff,j = 0,flag = 0; 21 for(int i = 1;i <= N;++i) 22 if(A[i] - A[j] >= S) 23 { 24 while(A[i] - A[j+1] >= S) j++; 25 if(i - j < ans) 26 { 27 flag = 1; 28 ans = i - j; 29 } 30 } 31 printf(flag? "%d ":"0 ",ans); 32 } 33 return 0; 34 } 35