POJ-2566 Bound Found(尺取+想法题)

题意

给定 n 个数,与 k s 值,求区间总和的绝对值最接近 s 的一个区间,并输出它的总和绝对值、左端点、右端点。
1 n 100000

思路

用尺取法必须要有单调性,总和的绝对值似乎找不到单调性,那么只能创造一个单调性。而直接将原数组排序,无法得到原来区间的总和。由此想到将原数组求前缀和再将前缀数组升序排序,那么任选两个数作差都对应着一个区间和的绝对值,又由于要输出区间的两个端点,所以要将前缀和与下标封成结构体排序。那么原问题就化为了一个升序区间中取两个数作差求最接近 s 的值。特别注意的是右推时要推到最佳的右端点,而且左右端点相差至少为 1

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
typedef long long LL;
using namespace std;
int a[100003];

struct node
{
    int v,pos;
    bool operator <(const node &_)const
    {
        return v<_.v;
    }
}s[100003];

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m),n||m)
    {
        FOR(i,1,n)scanf("%d",&a[i]);
        s[0].v=s[0].pos=0;
        FOR(i,1,n)
        {
            s[i].v=s[i-1].v+a[i];
            s[i].pos=i;
        }
        sort(s,s+1+n);
        FOR(i,1,m)
        {
            int t,R=0,ans=2e9+10,_L,_R;
            scanf("%d",&t);
            FOR(L,0,n)
            {
                while(L>=R||R<n&&abs(s[R+1].v-s[L].v-t)<=abs(s[R].v-s[L].v-t))R++;
                if(R>n)break;
                if(abs(s[R].v-s[L].v-t)<abs(ans-t))
                {
                    ans=s[R].v-s[L].v;
                    _L=min(s[L].pos,s[R].pos)+1;
                    _R=max(s[L].pos,s[R].pos);
                }
            }
            printf("%d %d %d\n",ans,_L,_R);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Paulliant/article/details/80866521