北大ACM3061——Subsequence

本文介绍了一种算法问题,即找到一个整数数列中最小的连续子序列,其和不小于给定整数S。提供了两种解决方案,一种时间复杂度为nlogn,另一种为n。详细解释了每种方法的实现思路,并附上了C++代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大概的意思是:给你一个整数数列,从中找出最小连续子序列的和不小于整数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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值