最近碰到好多对区间(数组)操作的题目。现在总结一下:
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;
}
缺陷,数组元素只能为非负数。也只能求最小区间。(个人理解)
分析:
如果
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;
}
2、对某一区间和满足某条件
条件类型:等于某一个值N或者是N的倍数。(我见到的)
方法:求出每一个sum[i],并将其取摸(%N),如果出现在前面出现同样大小的sum[j],则[j+1,i]是满足条件的区间。
题解:令代表女生的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;
}