poj 3061(二分 or 尺取法)

传送门:Problem 3061

https://www.cnblogs.com/violet-acmer/p/9793209.html

 马上就要去上课了,先献上二分AC代码,其余的有空再补

题意:

  给定长度为 n 的整数数列 a[0,1,2,........,n]以及整数 S。

  求出总和不小于 S 的连续子序列的长度的最小值。

  如果解不存在,则输出 0。

题解:

  1、二分

    由于所有的元素都大于 0 ,所以数组a[ ] 的前缀和sum[ ]为递增的序列,满足二分的条件。

    首先确定子序列的起点为start(start的可能取值为 1,2,......,n)。

    假设区间[start,end]是以start为子序列起点的最小区间,则需要满足 sum[end]-sum[start-1] >= S,而确定满足条件的最小的 end 用二分即可在O(longn)的时间完成。

    所以总的时间复杂度为 O(nlogn)

  2、尺取法

    相关说明:

      设以a[start]为子序列起点的总和最初大于S的连续子序列为a[start,......,end],此时 res = end-start+1;

    (1):end++,找到最大的 k ,使得在去除当前子序列的前 k 个数后依旧满足 sum[end]-sum[start-1 + k] >= S,并判断是否需要更新 res。

    (2):重复(2)过程,直到 end > n 为止。

AC代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 #define mem(a,b) (memset(a,b,sizeof(a)))
 6 const int maxn=1e5+50;
 7 
 8 int N,S;
 9 int a[maxn];
10 int sum[maxn];
11 int binarySearch(int val)
12 {
13     int l=0,r=N+1;
14     while(r-l > 1)
15     {
16         int mid=l+((r-l)>>1);
17         if(sum[mid] >= val)
18             r=mid;
19         else
20             l=mid;
21     }
22     return r;
23 }
24 int main()
25 {
26     int T;
27     scanf("%d",&T);
28     while(T--)
29     {
30         mem(sum,0);
31         scanf("%d%d",&N,&S);
32         int res=0;
33         for(int i=1;i <= N;++i)
34         {
35             scanf("%d",a+i);
36             sum[i] += sum[i-1]+a[i];
37         }
38         for(int s=1;s <= N;++s)
39         {
40             int t=binarySearch(sum[s-1]+S);
41             if(t != N+1)
42                 res=(res==0 || (t-s+1)<res ? t-s+1:res);
43         }
44         printf("%d\n",res);
45     }
46 }
二分
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<queue>
 4 using namespace std;
 5 #define pb push
 6 const int maxn=1e5+50;
 7 
 8 int N,S;
 9 int a[maxn];
10 queue<int >myqueue;
11 
12 int main()
13 {
14     int T;
15     scanf("%d",&T);
16     while(T--)
17     {
18         while(!myqueue.empty())
19             myqueue.pop();
20         scanf("%d%d",&N,&S);
21         for(int i=1;i <= N;++i)
22             scanf("%d",a+i);
23         int end=1;
24         int sum=0;
25         int res=0;
26         while(sum < S && end <= N)//先找到以1为序列起点的满足条件的最小的end
27         {
28             sum += a[end];
29             res++;
30             myqueue.pb(a[end++]);
31         }
32         if(sum >= S)
33         {
34             while(sum-myqueue.front() >= S)//找到满足条件的当前最小的范围
35             {
36                 sum -= myqueue.front();
37                 myqueue.pop();
38                 res--;
39             }
40             while(end <= N)
41             {
42                 myqueue.pb(a[end]);
43                 sum += a[end++];
44                 while(sum-myqueue.front() >= S)//步骤(2)
45                 {
46                     sum -= myqueue.front();
47                     myqueue.pop();
48                 }
49                 res= res > myqueue.size() ? myqueue.size():res;//判断是否更新 res
50             }
51         }
52         else
53             res=0;
54         printf("%d\n",res);
55     }
56 }
View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int maxn=1e5+50;
 6 
 7 int N,S;
 8 int a[maxn];
 9 
10 int Solve()
11 {
12     int start=1,end=1;
13     int res=0;
14     int sum=0;
15     while(1)
16     {
17         while(end <= N && sum < S)
18             sum += a[end++];
19         if(sum < S)
20             break;
21         res=(res == 0 || (end-start) < res ? end-start:res);
22         sum -= a[start++];
23     }
24     return res;
25 }
26 
27 int main()
28 {
29     int T;
30     scanf("%d",&T);
31     while(T--)
32     {
33         scanf("%d%d",&N,&S);
34         for(int i=1;i <= N;++i)
35             scanf("%d",a+i);
36         printf("%d\n",Solve());
37     }
38 }
尺取法&挑战程序设计竞赛

 

posted @ 2018-10-16 10:07  HHHyacinth  阅读(207)  评论(0编辑  收藏  举报