poj3601-Subsequence

题目链接

                                                                                 Subsequence

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 21313 Accepted: 9124

Description

A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, and a positive integer S (S < 100 000 000) are given. Write a program to find the minimal length of the subsequence of consecutive elements of the sequence, the sum of which is greater than or equal to S.

Input

The first line is the number of test cases. For each test case the program has to read the numbers N and S, separated by an interval, from the first line. The numbers of the sequence are given in the second line of the test case, separated by intervals. The input will finish with the end of file.

Output

For each the case the program has to print the result on separate line of the output file.if no answer, print 0.

Sample Input

2
10 15
5 1 3 5 10 7 4 9 2 8
5 11
1 2 3 4 5

Sample Output

2
3

Source

Southeastern Europe 2006

题目意思:给定长度为N的序列,让求出不小于S的连续子序列的长度的最小值。

储备知识:点击

尺取法其实也是一种模拟,是解决寻找区间和问题的一种方法。

假如有这么一个问题:给你一些数,请在这些数中找到一个区间,使得区间里每一个元素的和大于或等于给定的某个值。

不会尺取法的话,肯定就会开双重循环,枚举区间起点和终点,然后每一次都求一次和,再和给定的数作比较。

尺取法与它的思路类似,都是寻找一个区间的起点和终点。做法是:

用两个指针,最初都指向,这一组数中的第一个,然后如果这个区间的元素之和小于给定的数,就把右指针向右移,直到区间和大于等于给定的值为止。之后把左指针向右移,直到区间和等于给定的值为止,保存方案,继续操作。

假如左指针指向这些数的第一个,并且右指针指向这组数的最后一个,这种情况下的子区间元素之和仍然小于给定的数的话,那么就输出-1,表示不可能。

那么怎么求区间和呢?

当然,for一遍是可以的,但是太浪费时间了。我们可以引入一个累加器,初始值等于这组数中的第一个元素(因为最开始左指针和右指针都指向它),当右指针向右移时,累加器每次就加上右指针指向的元素的值。当左指针向右移时,累加器每次就减去左指针指向的值。

怎么实现呢?

-----------------------------------

思路:

wa代码:没有求到最小的

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue> 
#define in(x) scanf("%d",&x)
#define out(x) printf("%d\n",x)
#define rep(i,a,b) for(i=a;i<b;i++) 
using namespace std;
const int maxn=1e5+5;
int k[maxn];
int i,j;
int main()
{
	int t,n,s,sum,num,flag,sum2;
	in(t);
	while(t--){
		priority_queue<int>q;
		sum=0,num=0;
		in(n),in(s);
		rep(i,0,n){//输入n 
			in(k[i]);
			q.push(k[i]);
		}
		while(sum<s){
			int temp=q.top();
			q.pop();
			sum+=temp;
			num++;
		}
		//cout<<"0000 "<<num<<endl;
		sum=0,flag=0,sum2=0;
		while(1){
			rep(i,0,n){
				sum=0,sum2=0;
				rep(j,i,i+num){
					sum+=k[i];
					sum2++;
				}
				if(sum>=s){
					flag=1;
					break;
				}
			}
			if(flag) break;
			num++;
		}
		out(sum2);
	} 
} 

伪尺取法wa代码: 

#include<iostream>
#include<cstdio>
#include<algorithm>
#define in(x) scanf("%d",&x)
#define out(x) printf("%d\n",x)
#define rep(i,a,b) for(i=a;i<b;i++)
#define min(a,b) a<b?a:b
using namespace std;
const  int maxn=1e+5;
typedef long long int ll;
int n,s,k[maxn],smin,i,j;
ll sum=0;
void chiqu()
{
	int right,left;
	right=left=0;
	while(right<n&&left<n){
		sum=0;
		right=left;
		while(sum<s&&right<n){
			sum+=k[right++];
		}
		//cout<<"sum "<<sum<<endl;
		smin=min(smin,right-left+1);
		//cout<<"smin "<<smin<<endl;		
		if(right==n&&sum<s) return;
		else{
			while(sum>=s&&left<n){
				sum-=k[left++];
			}
			smin=min(smin,right-left+1);
		}
	}
}
int main()
{
	int t;
	in(t);
	while(t--){
		s=0,smin=999999;//smin的初始化 
		in(n),in(s);
		rep(i,0,n){
			in(k[i]);
		}
		chiqu();
		out(smin);
	}
}

ac代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[100000+33];
int ans,s,n;
void solve()
{
    ans=100000+44; 
    int l=0,r=0,sum=0,lsub=0;
    while(r<n){
        while(sum<s&&r<n){//尺子右边,向右移 
            sum+=a[r++];
            ++lsub;
        }
        while(sum>=s){//尺子左边,向右移 
            sum-=a[l++];
            --lsub;
        }
        ans=min(ans,lsub+1);
    }
}
int main()
{
    int T,i,sum;
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&s);
        sum=0;
		bool flag=false;
        for(i=0;i<n;++i){
            scanf("%d",&a[i]);
            sum+=a[i];
            if(sum>=s&&!flag) flag=true;    
        }
        if(!flag){
            printf("0\n");
			continue;
        }
        solve();
        printf("%d\n",ans);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值