【POJ】2566 - Bound Found 尺取法

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

给出一个整数列,求一段子序列之和最接近所给出的t。输出该段子序列之和及左右端点。

【思路】

……前缀和比较神奇的想法。一般来说,我们必须要保证数列单调性,才能使用尺取法。

预处理出前i个数的前缀和,和编号i一起放入pair中,然而根据前缀和大小进行排序。由于abs(sum[i]-sum[j])=abs(sum[j]-sum[i]),可以忽视数列前缀和的前后关系。此时,sum[r]-sum[l]有单调性。

因此我们可以先比较当前sum[r]-sum[l]与t的差,并更新答案。

如果当前sum[r]-sum[l] < t,说明和还可以更大,r++。

同理,如果sum[r]-sum[l]>t,说明和还可以更小,l++。

如果sum[r]-sum[l]=t,必定是最小答案。

【注意点】

由于序列不能为空,即l<>r,如果l=r则r++。

我们更新答案的时候左右区间端点为乱序,输出的时候调整一下。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
const int INF=1<<30;
int n,k,t;
pair <int,int> sum[100005];



int main(){
    while ((scanf("%d%d",&n,&k))!=EOF){
        sum[0]=make_pair(0,0);
        int tmp=0;
        for (int i=1;i<=n;i++){
            int x;
            cin >> x;
            tmp+=x;
            sum[i]=make_pair(tmp,i);
        }
        sort(sum,sum+n+1);
        while (k--){
            cin >> t;
            int Ans_l,Ans_r,Ans;
            int minAns=INF;
            int l=0,r=1;
            while (r<=n&&minAns){
                int delta=sum[r].first-sum[l].first;
                if (abs(delta-t)<=minAns){
                    minAns=abs(delta-t);
                    Ans=delta;
                    Ans_l=sum[l].second;
                    Ans_r=sum[r].second;
                }
                if (delta<t) r++;
                if (delta>t) l++;
                if (l==r) r++;
            }
            if (Ans_l>Ans_r) swap(Ans_l,Ans_r);
            cout << Ans << " " << Ans_l+1 << " " << Ans_r << endl;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值