区间/数组操作

本文探讨了针对区间和数组操作的常见问题,包括寻找满足特定条件的最小区间、对特定区间求和并满足条件的解法,以及如何找出最长上升子序列。通过实例和解析,为读者提供了清晰的思路和解决方案。

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

最近碰到好多对区间(数组)操作的题目。现在总结一下:

1、满足某条件的最小区间


题意:对于一个长度为N的数组,求出总和不小于S的连续子序列长度的最小值
方法①:O(N)求出sum,然后以O(1)的时间计算区间上的总和。 时间复杂度O(NlogN);
#include <iostream>
#include <algorithm>
using namespace std;
int sum[100010];
int main()
{
	std::ios::sync_with_stdio(false);
	int cas;
	cin>>cas;
	while(cas--)
	{
		memset(sum,0,sizeof(sum));
		int n,s;
		cin>>n>>s;
		int minn=n+2;
		for(int i=1;i<=n;i++)
		{
			cin>>sum[i];
			sum[i]+=sum[i-1];
		}
		if(sum[n]<s)
		{
			cout<<0<<endl;
			continue;
		}
		for(int i=0;sum[i]+s<=sum[n];i++)  //目标找出有sum[t]-sum[i]>=s.即sum[t]>=sum[t]+s;确保sum[t]存在。
		{
			int t=lower_bound(sum+1,sum+n,sum[i]+s)-sum;//找出>=sum[i]+s的坐标t,
			minn=min(minn,t-i);
		}
		cout<<minn<<endl;
	}
	return 0;
}

方法②:尺取法 时间复杂度 O(N)
缺陷,数组元素只能为非负数。也只能求最小区间。(个人理解)
分析:
如果
a[s]+a[s+1]+a[s+2]+········+a[t]>=s;
那么a[s+1]+a[s+2]+········+a[t]<a[s]+a[s+1]+a[s+2]+········+a[t],
即    a[s+1]+a[s+2]+········+a[t]<=s;
算法分析:
(1) 对sum,s,e初始化。
(2) 只要sum<s,sum不断加上a[e],e移动一位。
(3)当(2)停止后,若sum<s,break(已经没有进行的余地了)。否则sum>=s,更新minn;
(4)sum减去a[s],s向前移动一位。回到(2);
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
	std::ios::sync_with_stdio(false);
	int cas;
	cin>>cas;
	while(cas--)
	{
		int a[100010];
		int n,S,ans=0,minn=100010;
		cin>>n>>S;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
			ans+=a[i];
		}
		if(ans<S)
		{
			cout<<"0\n";
			continue;
		}
		int s=1,e=1,sum=0;  //s,e分别记录头位置与末位置。
		while(1)
		{
			while(e<=n&&sum<S)//循环N次。找到sum>=S,此时满足条件
				sum+=a[e++];
			if(sum<S)          //退出循环的条件。
				break;
			minn=min(minn,e-s);  //找答案
			sum-=a[s++];  //sum头位置,s上一格。
		}
		cout<<minn<<endl;
	}
	return 0;
}

poj3230 尺取法。

2、对某一区间和满足某条件

条件类型:等于某一个值N或者是N的倍数。(我见到的)
方法:求出每一个sum[i],并将其取摸(%N),如果出现在前面出现同样大小的sum[j],则[j+1,i]是满足条件的区间。
vijos p1195    
题解:令代表女生的0赋值为-1,则这道题变成求某区间的和等于0的倍数。
代码:
#include <iostream>
#include <map>
using namespace std;
int main()
{
	std::ios::sync_with_stdio(false);
	int a[100010];
	int n,maxn=0;
	cin>>n;
	map<int,int>m1;
	for(int i=1;i<=n;i++)
	{
		int k;
		cin>>k;
		if(k==1)
			a[i]=1;
		else
			a[i]=-1;
	}
	for(int i=1;i<=n;i++)
	{
		a[i]+=a[i-1];
		if(m1[a[i]]==0&&a[i])  //如果求N的倍数的话,标记第一次sum[i]出现的下标即可。
			m1[a[i]]=i;   //如果求某区间等于N,更新sum[i]的下标。
		else
			maxn=maxn>i-m1[a[i]]?maxn:i-m1[a[i]];
	}
	cout<<maxn<<endl;
	return 0;
}

3、最长上升序列

利用lower_bound()更新最长上升数组,时间复杂度O(NlogN),比朴素版(O(N*N))快多了,并且能求出若长度相等时末位最小的上升序列。
代码:
#include <iostream>
#include <algorithm>
using namespace std;
int dp[100001];
const int INF=100000000;
int main()
{
	int n;
	cin>>n;
	fill(dp,dp+n,INF);
	for(int i=1;i<=n;i++)
	{
		int a;
		cin>>a;
		*lower_bound(dp,dp+n,a)=a;
	}
	cout<<lower_bound(dp,dp+n,INF)-dp<<endl;
	return 0;
}

4、待续。。。。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值