POJ2566(尺取法+前缀和)

题意:给出一个整数列,求一段子序列之和最接近所给出的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]

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

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

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

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

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn=100005;
const double eps=1e-8;
const double PI = acos(-1.0);
#define p pair
ll myabs(ll x)
{
    return x>0?x:-x;
}
int main()
{
    int n,k;
    while(scanf("%d%d",&n,&k))
    {
        int a[maxn];
        p sum[maxn];
        sum[0]=p(0,0);
        if(!n&&!k)
            break;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            sum[i]=p(a[i]+sum[i-1].first,i);
        }
        sort(sum,sum+n+1);
        
        while(k--)
        {
            ll l=0,r=1,l1,r1,ans,t,tmp=inf;
            scanf("%lld",&t);
            while(r<=n)
            {
                ll b=sum[r].first-sum[l].first;
                if(myabs(t-b)t)
                    l++;
                else if(br1)
                swap(l1,r1);
            printf("%lld %lld %lld\n",ans,l1+1,r1);
        }
    }
    return 0;
}

 

你可能感兴趣的:(尺取法)