题目大概的意思是:给你一个整数数列,从中找出最小连续子序列的和不小于整数S。
数列最小为10,最大为100000,用暴力法估计会超时,我没有试过。
这里,我将用两种方法来求解这一题,一个时间复杂度为nlogn,另一个时间复杂度为n,不过两个提交了,时间都是79MS。没多大的区别。
1.时间复杂度为nlogn的方法:
主要的思路是用一个数组 sum 算出数列的前 i+1 个数的和,(i从0到n),然后每一个数列sum的每一个都加上S,再在数组sum中查找不小于sum[i] + S的位置ans。ans - i 就是不小于S的序列的长度,不断更新ans - i,就可以将解求出。
algorithm 中有一个函数lower_bound 就是求解一段数组中不小于一个数的下标。
下面的是AC的代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int a[100005], sum[100005]; int min(int x, int y) { return x > y ? y : x; } void solve(int n, int s) { int i, j; for(i = 0; i < n; i++) //计算前i+1个数的和 sum[i + 1] = sum[i] + a[i]; if(sum[n] < s) //全部数之和小于s,直接输出0 printf("0\n"); else { int res = n; for(j = 0; sum[j] + s <= sum[n]; j++) //在sum数组中,查找不小于s + sum【j】的位置,不断的更新 { int ans = lower_bound(sum + j, sum + n, sum[j] + s) - sum; //返回不小于s + sum【j】的位置 res = min(res, ans - j); //比较res与ans - j谁小。不断更新 } printf("%d\n", res); } } int main() { int t, n, s; scanf("%d", &t); while(t--) { scanf("%d%d", &n, &s); memset(sum, 0, sizeof(sum)); //初始化, for(int i = 0; i < n; i++) scanf("%d", &a[i]); solve(n, s); } return 0; }
2.时间复杂度为n的方法:
主要的思路就是:设置两个位置的标志,一个s为不小于m的序列的起始位置,一个t为不小于m的序列的末尾位置。一开始s = t = 0,设置一个序列和sum = 0;开始 sum += a【t++】直到 sum > m,算出序列长度 t - s;然后sum - a【s++】;看sum 是否还大于m,不大于,就继续加上a【t++】,否则则更新序列长度,sum - a【s++】;
不断的这样更新,就可以求出最小的序列长度。
对于文字不是很理解,可以看下面的图示。
下面的是AC的另一段代码:
#include <iostream> #include <cstdio> using namespace std; int a[100005]; int min(int x, int y) { return x > y ? y : x; } void solve(int n, int m) { int res = n + 1; int s, t, sum; s = t = sum = 0; while(1) { while(t < n && sum < m) //累加a数组,直到sum > m { sum += a[t++]; } if(sum < m) //如果总和小于m,则退出 break; res = min(res, t - s); //算出序列长度 sum -= a[s++]; //序列起始位置去掉一个,再进行循环 } if(res > n) res = 0; printf("%d\n", res); } int main() { int t, n, s; scanf("%d", &t); while(t--) { scanf("%d%d", &n, &s); for(int i = 0; i < n; i++) scanf("%d", &a[i]); solve(n, s); } return 0; }
时间: 2024-10-25 19:30:39