[POJ 2566] Bound Found (TwoPointer入门)

POJ - 2566
给你一组数,要求从中找出连续的一段,使得其和的绝对值尽量和 t接近

这种求序列连续一段的情况,最适合使用 two_pointer的技巧了
不过 two_pointer的前提是,序列在某种意义上是单调的
不过这题难就难在序列里数可正可负,时大时小,所以并不能使用 two_pointer

关键步骤,求出前缀和,再对前缀和排序
由于求的是绝对值,那么 sum[r]-sum[l]==sum[l]-sum[r]
所以对前缀和排序不影响结果
预处理出前缀和,然后连同 0位置的前缀和一起从小到大排序,这样序列就单调了
问题就归约成了单调序列中两个不相同的数相减,从而可以使用 two_pointer

注意点:
1) 要将 0位置的前缀和一起排序
2) 由于序列不能为空,所以必须是两个位置不相同的前缀和相减
3) sum[r]-sum[l]求出来的是 [l+1, r]的,所以最后输出的时候 l还要加一

实际上,two_pointer是一种较为机智的暴力
而且很多 two_pointer由于需要单调性,所以用二分也可以做
而且不知怎的,我从 two_pointer联想到了莫队算法 = =

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Pow2(a) (a*a)

const int maxn=1e5+10,INF=0x3f3f3f3f;
int N,K;
Pii inpt[maxn];

bool pcmp(Pii u, Pii v){return u.first<v.first;}

int main()
{
    while(~scanf("%d%d",&N,&K)&&N)
    {
        for(int i=1; i<=N; i++)
        {
            scanf("%d", &inpt[i].first);
            inpt[i].first+=inpt[i-1].first;
            inpt[i].second=i;
        }
        inpt[N+1].first=0;inpt[N+1].second=0;
        sort(inpt+1,inpt+1+N+1,pcmp);
        for(int i=1; i<=K; i++)
        {
            int t;
            scanf("%d", &t);
            int sp=1,tp=1,delt=INF,ans,ansl,ansr;
            for(sp; sp<=N+1; sp++)
            {
                if(tp<=sp) tp=sp+1;
                for(tp; tp<=N+1; tp++)
                {
                    int val=inpt[tp].first-inpt[sp].first;
                    if(abs(val-t)<delt)
                    {
                        delt=abs(val-t);
                        ans=val;
                        ansl=inpt[sp].second;
                        ansr=inpt[tp].second;
                    }
                    if(val>t) break;
                }
            }
            printf("%d %d %d\n", ans, min(ansl,ansr)+1, max(ansl,ansr));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值