POJ3061Subsequence(二分法and尺取法)

本文介绍了解决给定数字序列中找到和大于或等于目标值S的最短连续子序列的问题。提供了两种算法实现:二分查找法与尺取法,并附带AC代码。

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

题目大意:给你n个数字,一个终点数S,求这n个数字(顺序不能改变)中截取k个数字使之和大于或等于这个数S

解题思路:

1. 二分法:

输入数字的时候对设置一个辅助数组进行预处理,即在i位置的时候使这个数组保存着从开头到目前这个区间所有数字的累和,目的是什么呢?当你知道一个区间的头和尾的时候你直接用尾部位置的数减去头部前一个位置的数得到的就是本区间的数字累和。然后就是寻找头部和尾部,用一个循环枚举头的情况,而尾部的情况就可以利用二分查找,其实这个预处理过的数组一定是升序的。复杂度是O(nlogn)。POJ上360ms

2.尺取法:

其实思路很类似,都是寻找这个区间,尺取法则是先初始化头尾在同一个位置,然后把尾部往后挪,知道sum(区间数累和)大于等于S时更新Min(区间长度),然后头部往后挪,在表上演示步骤时就像是一个会走动的区间。在《挑战程序设计竞赛》第二版P146-P150左右有这个算法的四个步骤。和我描述的大同小异。

二分法:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

int book[100010],n,S;

int find()
{
	int i,t,Min=100011;
	for(i=0;S<=book[n]-book[i];i++)
	{
		t=lower_bound(book+i,book+n+1,book[i]+S)-book-i;
		if(t<Min)
			Min=t;
	}
	return Min;
}

int main()
{
	int i,j,t;
	cin>>t;
	while(t--)
	{
		cin>>n>>S;
		memset(book,0,sizeof(book));
		for(i=1;i<=n;i++)
		{
			cin>>book[i];
			book[i]+=book[i-1];
		}
		if(book[n]>=S)
			cout<<find()<<endl;
		else
			cout<<"0"<<endl;
	}
	return 0;
}

尺取法:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

int book[100010],n,S;

int find()
{
	int head,tail,Min=100011,sum;
	head=tail=1;
	sum=0;
	while(1)
	{
		while(tail<=n && S>sum)
			sum+=book[tail++];
		if(S>sum)
			break;
		if(Min>tail-head)
			Min=tail-head;
		sum-=book[head++];
	}
	if(Min>n)
		Min=0;
	return Min;
}

int main()
{
	int i,j,t;
	cin>>t;
	while(t--)
	{
		cin>>n>>S;
		memset(book,0,sizeof(book));
		for(i=1;i<=n;i++)
			cin>>book[i];
		cout<<find()<<endl;
	}
	return 0;
}

这个也是AC代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

int book[100010],n,S;

int find()
{
	int i,head,tail,Min=100011;
	head=tail=0;
	while(1)
	{
		while(tail<=n && S>book[tail]-book[head])
			tail++;
		if(tail>n)
			break;
		else if(Min>tail-head)
			Min=tail-head;
		head++;
	}
	return Min;
}

int main()
{
	int i,j,t;
	cin>>t;
	while(t--)
	{
		cin>>n>>S;
		memset(book,0,sizeof(book));
		for(i=1;i<=n;i++)
		{
			cin>>book[i];
			book[i]+=book[i-1];
		}
		if(book[n]>=S)
			cout<<find()<<endl;
		else
			cout<<"0"<<endl;
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小胡同的诗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值