您的位置:首页 > 产品设计 > UI/UE

POJ 3061 Subsequence(二分/尺取法)

2015-09-02 19:46 525 查看
题目链接:

http://poj.org/problem?id=3061

解题思路:

题目大意:

给定长度为n的整列整数a[0],a[1],……a[n-1],以及整数S,求出总和不小于S的连续子序列的长度的最小值。

由于所有的元素都大于零,如果子序列[s,t)满足a[s]+...+a[t-1]>=S,那么对于任何t <t'一定有a[s]+...a[t'-1]>=S。此外对于区间[s,t)上的总和来说如果令s

um[i]=a[0]+a[1]+...+a[i-1]那么a[s]+a[s-1]+...a[t-1]=sum[t]-sum[s],因此预先以O(n)的时间计算好sum的话,就可以以O(1)的时

间计算区间上的总和。这样一来,子序列的

起点s确定以后,便可以用二分搜索快速地确定使序列和不小于S的结尾t的最小值。

AC代码(二分法):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 100005;
int n,s;
int a[maxn];
int sum[maxn+1];

void solve(){
    //计算sum
    for(int i = 0; i < n; i++)
        sum[i+1] = sum[i]+a[i];
    if(sum
 < s){
        printf("0\n");
        return ;
    }
    int res = n;
    for(int i = 0; sum[i] + s <= sum
;i++){
        //利用二分搜索求出t
        int t = lower_bound(sum+i,sum+n,sum[i]+s)-sum;
        res = min(res,t-i);
    }
    printf("%d\n",res);
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&s);
        for(int i = 0; i < n; i++)
            scanf("%d",&a[i]);
        solve();
    }
    return 0;
}


这个时间复杂度是O(nlogn),虽然足以解决这个问题,但我们还可以更高效地解决。我们设以a[s]开始的总和最初大于S时的连续子序列为a[s]+a[s+1]+……a[t-1],这时,

a[s+1]+a[s+1]+……a[t-2]<a[s]+a[s+1]+……a[t-2]<S,所以如果从a[s+1]开始总和最初超过S的连续子序列是a[s+1]+……a[t‘-1],则t<=t'。

故可以设计如下算法:

(1)初始s=t=sum=0

(2)只要依然有sum<S,就不断将sum加上a[t],并且t=t+1;

(3)如果(2)中无法满足sum>=s则终止,否则ans=min(ans,t-s);

(4)将sum减去a[s],s增加1然后回到(2).

对于这个算法,因此t最多变化n次,因此只需O(n)的复杂度就可以求解这个问题了。

5 1 3 5 10 7 4 9 2 8



5 1 3 5 10 7 4 9 2 8



5 1 3 5 10 7 4 9 2 8



5 1 3 5 10 7 4 9 2 8



5 1 3 5 10 7 4 9 2 8



5 1 3 5 10 7 4 9 2 8



5 1 3 5 10 7 4 9 2 8



5 1 3 5 10 7 4 9 2 8

AC代码(尺取法):

#include <iostream>
#include <cstdio>
using namespace std;

int n,S;
int a[100005];

void solve(){
    int res = n+1;
    int s = 0,t = 0,sum = 0;
    while(1){
        while(t < n && sum < S){
            sum += a[t++];
        }
        if(sum < S)
            break;
        res = min(res,t-s);
        sum -= a[s++];
    }
    if(res > n){
        //解不存在
        res = 0;
    }
    printf("%d\n",res);
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&S);
        for(int i = 0; i < n; i++)
            scanf("%d",&a[i]);
        solve();
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: