poj3061尺取法/前缀和 二分(java)

今天遇到这题因为以前没见到过,当时就是想着应该有着一个很简单的方法可以过但是奈何就是没思路。后来看了别人思路写了下来。学习了尺取法

poj3061

题目介绍:

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

思路的萌发:

  1. 最开始的傻D思路:暴力破解,从长度1开始暴力。o(n3)超时。
  2. 前缀和 二分:把前面的和用sum[]求出来。然后从第一项到最后一项一个个求,因为有sum[],所以每一个都用二分查找,然后剪枝考虑省去一些不必要的步骤。可以获得答案。复杂度o(nlogn);
  3. 尺取法:这个方法非常巧妙,复杂度是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();

	}

}

  • 如果对后端、爬虫、数据结构算法等感性趣欢迎关注我的个人公众号交流:bigsai
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员bigsai

喝杯咖啡压压惊!

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

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

打赏作者

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

抵扣说明:

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

余额充值