POJ2566(尺取法+前缀和)

博客介绍了如何利用尺取法解决POJ2566问题,即找到一个整数序列中和最接近给定目标t的子序列。首先,通过预处理得到数列的前缀和并排序。然后,通过维护左右指针l和r,不断更新最接近目标和的子序列及其端点。当子序列之和小于目标时,右指针r右移;反之,左指针l右移。若找到相等的情况,则为最小答案。最终,调整输出子序列的顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给出一个整数列,求一段子序列之和最接近所给出的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<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
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,int>
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)<tmp)
                {
                    tmp=myabs(t-b);
                    ans=b;
                    l1=sum[l].second;
                    r1=sum[r].second;
                }
                if(b>t)
                    l++;
                else if(b<t)
                    r++;
                else
                    break;
                if(l==r)
                    r++;
            }
            if(l1>r1)
                swap(l1,r1);
            printf("%lld %lld %lld\n",ans,l1+1,r1);
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值