子序列问题第一题,题目中给的n的范围为10<n<=1000,时间复杂度可以取到O(N³),采用暴力法,看代码
#include<bits/stdc++.h>
using namespace std;
main()
{
int n,s,sum;
int A[10010],minn;
while(scanf("%d%d",&n,&s)!=EOF)
{
minn=1e9;
for(int i=1; i<=n; i++)
{
scanf("%d",&A[i]);
}
for(int l=1; l<=n; l++) //子序列的左端点
{
for(int r=l; r<=n; r++)//子序列的右端点
{
int sum=0;
for(int k=l; k<=r; k++) //左端点到右端点的和
{
sum+=A[k];
}
if(sum>=s)
minn=min(minn,r-l+1); //左端点到右端点的字符个数
}
}
if(minn<1e9)
printf("%d",minn);
else
printf("0\n");
}
return 0;
}
子序列问题第一题,题目中给的n的范围为10<n<=10000,n的范围增加,时间复杂度至少要降到O(N²),看代码
#include<bits/stdc++.h>
using namespace std;
int pre[10010]; //pre数组为全局变量,pre数组中的值全为0
main()
{
int n,s,sum;
int A[10010],minn;
while(scanf("%d%d",&n,&s)!=EOF)
{
minn=1e9;
for(int i=1; i<=n; i++) scanf("%d",&A[i]);
for(int i=1; i<=n; i++) pre[i]=pre[i-1]+A[i]; //前缀和,该方法定义pre[i]为A[i]中前i个数的和
printf("%d",pre[0]);
for(int l=1; l<=n; l++) //左端点
{
for(int r=l; r<=n; r++)//右端点
{
int sum=0;
sum=pre[r]-pre[l-1];
if(sum>=s)
minn=min(minn,r-l+1);
}
}
if(minn<1e9)
printf("%d\n",minn);
else
printf("0\n");
}
return 0;
}
题目不变,当数据量再次扩大到100000时,必须要再次优化代码,将时间复杂度降到(nlogn),这里采用二分法
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int A[N],pre[N];
int n,s;
bool judge(int x) //判断当子序列长度为x时,是否存在长度为x的子序列的和>s
{
for(int l=1;l+x-1<=n;l++)//从1开始枚举x个连续的值,判断是否大于s
{
int r=l+x-1;
int sum=pre[r]-pre[l-1];
if(sum>=s) return 1;
}
return 0;
}
int main()
{
while(scanf("%d%d",&n,&s)!=EOF)
{
for(int i=1;i<=n;i++) scanf("%d",&A[i]);
for(int i=1; i<=n; i++) pre[i]=pre[i-1]+A[i]; //前缀和的公式
int l=1,r=n,mid;
while(l<r) //二分法,判断长度为mid时,是否存在子序列和大于s,这里时间复杂度为nlogn
{
mid=(l+r)/2;
if(judge(mid))
r=mid;
else
l=mid+1;
}
if(l==n&&pre[n]<s)
printf("0\n");
else
printf("%d\n",l);
}
return 0;
}