子序列分析
原题
给定长度为n的整数数列:a0,a1,..,an-1,以及整数S。这个数列会有连续的子序列的整数总和大于S的,求这些数列中,最小的长度。
分析
如果只是像题目这样的描述,没有强调正数,可以采用O(n^2)的方法。----------解法一
但是,很多同学在讨论的时候,指出了如果是正数,解法将会有什么样的变化。这个很好。不考虑正负的O(n^2)的方法,这里不详细说了,我们来讨论,当数列中都是正数的情况。
介绍一个利用排序+二分的方法。对于子序列ai...at,子序列和s=ai+...+at=sum[t]-sum[i-1]。sum[t]表示数列a0...at的和。那么,数组sum天然就是递增的,可以进行二分查找。 那么如何进行二分查找呢?对于数组sum,遍历找到第一个k,sum[k]>S,二分查找k前面的某一个j,j是sum[k]-sum[j]>S里最大的一个,则k-j是最小的。依次遍历完数组。 可以得到最小的长度,整体的时间复杂度O(nlogn),空间复杂度为O(n)。------解法二
是否有更快的方法呢?从以上两个方法,我们可以有如下的观察:---------解法三
-
a0...at>S,则a0...at+1无需再考虑
-
对于a0...at>S,只需尝试a1...at是否>S,如果大于S,则更新最短长度。
-
如果不大于S,大于S的只可能是a1...atat+1等。
鉴于以上的观察,我们有如下的算法:设置索引i,j指向第一个整数:
-
++j,直到sum[j]-sum[i]>S,这里不需要额外保存sum,为了方便说明。记录子序列长度
-
++i,如果sum[j]-sum[i]>S,更新最小子序列长度。直到sum[j]-sum[i]<=S。
-
++j,直到sum[j]-sum[i]>S。重复上面的两步,直到数组遍历完毕。
整个算法的时间复杂度为O(n)。下面我们做一个示例数组为{5,1,3,5,10,7,4,9,2,8},S=10,i=j=0开始
-
当j=3时,和为14>10,则更新最小长度为4
-
对i进行递增操作,和为9<10,不满足条件。对j进行递增
-
当i=1,j=4时,和为19>10,长度为4,不更新最小长度。递增i,直到i=3,此时和15>10,更新最小长度为2,
-
依次类推
最终得到最小长度为2.
【分析完毕】
附:
解法一代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n, s, i, j, tmp, count = 0, tmp_count, flag = 0;
scanf("%d %d", &n, &s);
int *a = (int *)malloc(sizeof(int) * n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
for (i = 0; i < n; i++)
{
tmp = 0, tmp_count = 0;
for (j = i; j < n; j++)
{
if (tmp < s)
{
tmp += a[j];
tmp_count++;
}
else
{
tmp_count++;
if (flag == 0)
{
count = tmp_count;
flag = 1;
}
else if (tmp_count < count)
{
count = tmp_count;
}
break;
}
}
}
printf("number is:%d\n", count);
return 0;
}
解法二:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n, s, i, j, tmp_sum = 0, count = 0, tmp_count;
scanf("%d %d", &n, &s);
int *a = (int *)malloc(sizeof(int) * n);
int *sum = (int *)malloc(sizeof(int) * n);
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
tmp_sum += a[i];
sum[i] = tmp_sum;
}
for (i = 0; i < n; i++)
{
if (sum[i] > s)
{
if (count == 0)
count = i;
tmp_count = i;
for (j = 0; j < i; j++)
{
if (sum[i] - sum[j] > s)
tmp_count = i - j;
else
{
if (tmp_count < count)
count = tmp_count;
break;
}
}
}
}
printf("%d\n", count);
return 0;
}
解法三: