UVA之1121 - Subsequence

简介:

【题目】

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 

Many test cases will be given. 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 there isn't such a subsequence, print 0 on a line by itself.

Sample Input 

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

Sample Output 

2 
3

【分析】

本题最直接的思路是二重循环,枚举子序列的起点和终点。代码如下(输入数据已存入数组A[1]~A[n])。

 

int ans = n+1;
for(int i = 1; i <= n; i++)
  for(int j = i; j <= n; j++) {
    int sum = 0;
    for(int k = i; k <= j; k++) sum += A[k];
    if(sum >= S) ans = min(ans, j-i+1);
  }
printf("%d\n", ans == n+1 ? 0 : ans);

很可惜,上述程序的时间复杂度是O(n3)的,因此,当n达到100 000的规模后,程序将无能为力。有一个方法可以降低时间复杂度,即常见的前缀和技巧。令Bi=A1+A2++Ai,规定B0=0,则可以在O(1)时间内求出子序列的值:Ai+Ai+1+…+Aj=Bj-Bi-1。这样,时间复杂度降为O(n2),代码如下。

 

B[0] = 0;
for(int i = 1; i <= n; i++) B[i] = B[i-1] + A[i];
int ans = n+1;
  for(int i = 1; i <= n; i++)
    for(int j = i; j <= n; j++)
      if(B[j] - B[i-1] >= S) ans = min(ans, j-i+1);
printf("%d\n", ans == n+1 ? 0 : ans);

遗憾的是,本题的数据规模太大,O(n2)时间复杂度的算法也太慢。不难发现,只要同时枚举起点和终点,时间复杂度不可能比O(n2)更低,所以必须另谋他路。比如,是否可以不枚举终点,只枚举起点,或者不枚举起点,只枚举终点呢?

我们首先试试只枚举终点。对于终点j,我们的目标是要找到一个让Bj-Bi-1≥S,且i尽量大(i越大,序列长度j-i+1就越小)的i值,也就是找一个让Bi-1≤Bj-S最大的i。考虑图1-29所示的序列。


当j=5时,B5=12,因此目标是找一个Bi-1≤12-7=5的最大i。注意到B是递增的(别忘了,本题中所有Ai均为整数),所以可以用二分查找。


【代码】

/*********************************
*   日期:2014-5-14
*   作者:SJF0115
*   题号: 1121 - Subsequence
*   地址:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=246&page=show_problem&problem=3562
*   来源:UVA
*   结果:Accepted
**********************************/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;

#define N 100001

int A[N];
int B[N];

//二分查找最接近target但不大于target
int BinarySerach(int target,int R){
    int L = 0;
    int mid = 0;
    while(L < R){
        mid = L + (R - L) / 2;
        if(B[mid] > target){
            R = mid;
        }
        else{
            L = mid + 1;
        }
    }
    return L;
}

int main(){
    int n,s,i,j;
    //freopen("C:\\Users\\wt\\Desktop\\acm.txt","r",stdin);
    while(scanf("%d %d",&n,&s) != EOF){
        int minLen = n+1;
        B[0] = 0;
        for(i = 1;i <= n;i++){
            scanf("%d",&A[i]);
            //序列前缀和
            B[i] = B[i-1] + A[i];
        }
        for(j = 1;j <= n;j++){
            int target = B[j] - s;
            //二分查找
            int index = BinarySerach(target,j-1);
            if(index > 0){
                minLen = min(minLen,j-index+1);
            }
        }
        //没有满足条件的序列
        if(minLen == n+1){
            minLen = 0;
        }
        cout<<minLen<<endl;
    }//while
    return 0;
}


【相似题目】

编程之美之2.14 求数组的子数组之和的最大值


目录
相关文章
uva10038 Jolly Jumpers
uva10038 Jolly Jumpers
45 0
[UVa OJ] Longest Common Subsequence
This is the classic LCS problem. Since it only requires you to print the maximum length, the code can be optimized to use only O(m) space (see here).
781 0
|
存储 固态存储
|
机器学习/深度学习
uva 11538 Chess Queen
点击打开链接 题意:给定一个n*m的矩阵,问有多少种方法放置两个相互攻击的皇后?规定在同一行同一列和同对角线的能够相互攻击 思路: 1 先考虑同一行的情况,n行就有n种情况,每一行有m*(m-1)种,总的是n*m*(m-1); 2 考虑同...
822 0
uva 10405 Longest Common Subsequence
#include&lt;iostream&gt; #include&lt;cstdio&gt; #include&lt;cstring&gt; using namespace std; char str1[1002],str2[1002]; int d[1002][1002]; int main() { while(gets(str1) &amp;&amp; gets(str2))
880 0
uva 1121 - Subsequence
点击打开链接uva 1121 思路:二分查找 分析: 1 题目要求找到一个最短的子序列长度并且这个子序列的和大于等于给定的s 2 如果按照常规的做法枚举起点和终点的话肯定是会超时的。
935 0
uva 10317 Equating Equations
点击打开链接uva 10317 思路:搜索 分析: 1 给定一个等式判断两边是否相等,如果一个等式相等那么通过移项到同一边可以得到正数的和等于负数 2 那么通过分析1我们可以知道我们可以求出这个等式的所有数字的和,判断和是否为偶数。
768 0
uva562Dividing Coins
题意:给出n枚硬币,每个硬币有自己的面值,将这些硬币均分,有时无法均分,求分出来的两份硬币的最小差值 分析:统计总面值然后用总面值的一半作为背包容量,01背包。 代码: View Code 1 #include 2 #include 3 #include 4 #i...
647 0
|
人工智能 BI JavaScript
uvalive2678Subsequence
题意:有n个正整数组成一个序列,给定整数S,求长度最短的连续序列,使得他们的和大于等于S 分析:设输入的序列为A[i], i=1..n, 构造前缀数组B[j], j=1..n, B[j]=B[j-1]+A[j], 规定B[0]=0, 当B[j]-B[i-1]>=s的时候i增加,直至B[j]-B[i...
661 0

热门文章

最新文章