今天遇到这题因为以前没见到过,当时就是想着应该有着一个很简单的方法可以过但是奈何就是没思路。后来看了别人思路写了下来。学习了尺取法
题目介绍:
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.
(给出N个正整数(10 <100 000)的序列,每个正整数小于或等于10000,并且给出正整数S(S <100 000 000)。编写程序以找到序列的连续元素的子序列的最小长度,其总和大于或等于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.
(第一行是测试用例的数量。对于每个测试用例,程序必须从第一行读取数字N和S,以间隔分隔。序列的编号在测试用例的第二行中给出,以间隔分开。输入将以文件结尾结束。)
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
思路的萌发:
最开始的傻D思路:暴力破解,从长度1开始暴力。o(n3)超时。
前缀和 二分:把前面的和用sum[]求出来。然后从第一项到最后一项一个个求,因为有sum[],所以每一个都用二分查找,然后剪枝考虑省去一些不必要的步骤。可以获得答案。
复杂度o(nlogn);
尺取法:这个方法非常巧妙,复杂度是o(n),他核心思想就是用一个段长当作可移动伸缩的尺子(左,右点向右移动)。求最短长就相当于 一个卷尺从头开始移动到各个点拉长,在这过程中。如果尺子长度大于需要的长,那么左面一直往前面移动,并且做相应记录,知道短了右侧需要继续加长。这种情况可以遍历所有满足的情况,复杂度非常低。
所有这题的尺取法就是 用一个sum从开始记录和,尺取法跑一圈
二分:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.StreamTokenizer; import java.util.Scanner; public class poj3601 { /* * 二分法 */ public static void main(String[] args) throws IOException { // TODO 自动生成的方法存根 StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out)); in.nextToken();int t=(int)in.nval; for(int i=0;i<t;i++) { in.nextToken();int a=(int)in.nval; in.nextToken();int b=(int)in.nval; int c[]=new int[a]; int len=Integer.MAX_VALUE; long sum[]=new long[a+1];// for(int j=0;j<a;j++) { in.nextToken();c[j]=(int)in.nval; sum[j+1]+=sum[j]+c[j]; } int left=0;int right=a; for(int j=0;j<a;j++) { int l=j; int r=(len==Integer.MAX_VALUE?a:(len+l>a?a:len+l));//右面长度存在不,不存在从a二分。如果存在加上这个点保证不能越界 boolean jud=false;//用于终止循环 while(true) { if(sum[r]-sum[l]>=b)//如果大于目标进行二分 { if(r-l<len) {len=r-l;} r=(l+r)/2; } else//小于目标直接向右增加寻找目标,找到的第一个就是极限最短,在这区间不能越界,长度不能超多已经知道最短长度 { for(;r<=a+1;r++) { if(r>a) {jud=true;break;} if(r-l>=len) {jud=true;break;} if(sum[r]-sum[l]>=b) {len=r-l;break;} } } if(jud) {break;} } } if(len!=Integer.MAX_VALUE) {out.println(len);} else out.println(0); } out.flush(); } }
尺取法:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.StreamTokenizer; import java.util.Scanner; public class poj3602尺取 { /* * 尺取法 */ public static void main(String[] args) throws IOException { // TODO 自动生成的方法存根 StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out)); in.nextToken();int t=(int)in.nval; for(int i=0;i<t;i++) { in.nextToken();int a=(int)in.nval; in.nextToken();int b=(int)in.nval; int c[]=new int[a]; int len=Integer.MAX_VALUE; long sum=0; for(int j=0;j<a;j++) { in.nextToken();c[j]=(int)in.nval; } sum=c[0];int l=0;int r=1; for(int j=1;j<a;j++) { sum+=c[j]; while(sum>b) { if(len>j-l) {len=j-l;} if(len<=0) {break;} sum-=c[l++]; } } if(len!=Integer.MAX_VALUE) {out.println(len+1);} else out.println(0); } out.flush(); } }