Bound Found(尺取-前缀和)

题目链接:https://cn.vjudge.net/contest/279225#problem/I

题目大意:就是给n个有正有负的整数,然后给k个t,在n数列中找到连续的一个序列使得这个序列的和的绝对值最接近t,然后输出这个序列的和的绝对值和它的区间左右端点。

思路:这是个尺取的问题,首先这是我第一道尺取题,不知道为什么第一道就不容易找单调出序列,不知道挂题的学长怎么想的,虽然当时讲了一小下这种问题,但是我没怎么很明白,也就是不会实现。然后刚刚看了大佬的博客才知道怎么回事,我都没有找到详解,还是自己调试看的,嗯,还是太弱了,不知道为啥,就是看不懂想不明白所以只能自己调试一下了。

我的想法就是,首先找到这个单调的序列嘛,然后就想到前缀和嘛,这个我还是明白的,因为之前就接触过前缀和这种东西,所以这个也很好说,但是呢,怎么利用这个前缀和来实现我们的目的?这就是个问题了。我就是这里一直没懂。

首先我们定义一个结构体,用来存这个n个整数的值、起始位置和前缀和,要注意这个位置是不能动的,因为我们最后要输出子序列的区间端点。还有就是,这n个数的信息要从1到n来存,因为说这个开始的0我们要存0,不然如果我们是从1-某个num的区间的话,我们就没办法实现,因为那样的话我们只能从2-某个num的区间。这儿也很好理解。然后我们就要开始进行我们的尺取了。首先从0 到1,这里的0 和1是指的我们排好序之后的结构体的顺序的下标,不是我们开始输入的位置,然后这时候就从左边开始尺取嘛,首先长度为1,然后如果这两个区间的差值temp(这个一定是非负数,因为前缀和从小到大排序嘛,所以大的减小的肯定是非负数)和t的dif的比min_(min_就是区间和的绝对值和t的差值的最小值)小,那么就让dif和min_交换,然后记录下来区间的左右端点值,这儿呢,就有一点需要注意,因为我们前面根据前缀和排了序,所以开始的id是乱排的,所以可能right要比left小,所以这里需要判断一下,总之,ansL=小的那个端点+1,ansR是大的那个端点,为啥是这样的其实很好理解,想一下,假设是4前缀和-3前缀和,那么这个区间就只包含一个元素就是4对应的元素,所以这个区间其实是4-4,嗯。然后呢,就是让这两个区间的差值temp和t比较,如果temp和t相等,那就是最优的区间,直接break输出结果,如果temp大,那就是我们尺取的长了,需要变短,然后就让left+1,然后如果temp小,就是我们尺取的不够长,就让right+1,这里还需要注意我们不能判断空区间,所以如果这个left等于right的话,那就让right++。如果中间没有break,那就一直到尺子到最后取完。然后就输出结果,基本就是这样了。嗯,千万别觉得我啰嗦,我说的如此详细呜呜呜,别嫌弃我。嗯,我本来就是拖延症加啰嗦加各种小毛病呜呜呜,没事,头发没掉光,我还能学!
说说我这道题的心塞之处,开始我用的long long,然后后边输入t的时候格式控制符写的是%d,于是一直输入的num是乱码,我看了好多遍才看到,哇,真的弱。
改了之后样例过了,我交!哇Compile Error,显示abs不能用于long long,然后我百度longlong取绝对值的函数,说是什么cmath或者cstdlib的llabs,又交!又是Compile Error,妈也,人家根本就没有好像,好像就只有long型的,然后我就改了int,哇,A了,真的无比心塞……现在还是不知道longlong 取绝对值什么鬼…………是真的迷……

#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#define INF 0x3f3f3f3f
using namespace std;
struct node
{
    int num;
    int id;
    int prefix_sum;
}a[100005];

bool cmp(node n1,node n2)
{
    return n1.prefix_sum<n2.prefix_sum;
}

int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k)&&(n||k))
    {
        a[0].num=0;
        a[0].id=0;
        a[0].prefix_sum=0;
        for(int i=1;i<n+1;i++)
        {
            scanf("%d",&a[i].num);
            a[i].id=i;
            a[i].prefix_sum=a[i-1].prefix_sum+a[i].num;
        }
        sort(a,a+n+1,cmp);//这儿呢,因为sort函数是左闭右开区间,所以就是区间长度是多少,右指针就是多少
        int t;
        while(k--)
        {
            scanf("%d",&t);
            int left=0,right=1,ansL,ansR,ans,temp,min_=INF;
            while(left<=n&&right<=n)
            {
                temp=abs(a[right].prefix_sum-a[left].prefix_sum);
                if(abs(temp-t)<min_)
                {
                    min_=abs(temp-t);
                    ans=temp;
                    ansL=a[left].id<a[right].id?a[left].id+1:a[right].id+1;
                    ansR=a[left].id<a[right].id?a[right].id:a[left].id;
                }
                if(temp<t)
                    right++;
                else if(temp>t)
                    left++;//这儿呢一定要是else if,不然的话如果是if的话,那么这句和下句的else就是一对,然后只要temp>t了就会break了,显然是不对的。
                else
                    break;
                if(left==right)
                    right++;
            }
            printf("%d %d %d\n",ans,ansL,ansR);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/86571473