• POJ_2566_Bound_Found_(尺取法+前缀和)


    描述


    http://poj.org/problem?id=2566

    给出一个整数序列,并给出非负整数t,求数列中连续区间和的绝对值最接近k的区间左右端点以及这个区间和的绝对值.

    Bound Found
    Time Limit: 5000MS   Memory Limit: 65536K
    Total Submissions: 2592   Accepted: 789   Special Judge

    Description

    Signals of most probably extra-terrestrial origin have been received and digitalized by The Aeronautic and Space Administration (that must be going through a defiant phase: "But I want to use feet, not meters!"). Each signal seems to come in two parts: a sequence of n integer values and a non-negative integer t. We'll not go into details, but researchers found out that a signal encodes two integer values. These can be found as the lower and upper bound of a subrange of the sequence whose absolute value of its sum is closest to t.

    You are given the sequence of n integers and the non-negative target t. You are to find a non-empty range of the sequence (i.e. a continuous subsequence) and output its lower index l and its upper index u. The absolute value of the sum of the values of the sequence from the l-th to the u-th element (inclusive) must be at least as close to t as the absolute value of the sum of any other non-empty range.

    Input

    The input file contains several test cases. Each test case starts with two numbers n and k. Input is terminated by n=k=0. Otherwise, 1<=n<=100000 and there follow n integers with absolute values <=10000 which constitute the sequence. Then follow k queries for this sequence. Each query is a target t with 0<=t<=1000000000.

    Output

    For each query output 3 numbers on a line: some closest absolute sum and the lower and upper indices of some range where this absolute sum is achieved. Possible indices start with 1 and go up to n.

    Sample Input

    5 1
    -10 -5 0 5 10
    3
    10 2
    -9 8 -7 6 -5 4 -3 2 -1 0
    5 11
    15 2
    -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
    15 100
    0 0
    

    Sample Output

    5 4 4
    5 2 8
    9 1 1
    15 1 15
    15 1 15
    

    Source

    分析


    尺取法.

    一眼看过去就是用尺取法找和t绝对值相差最小的区间和,但是这道题里的序列并不是非负的,这意味着固定左端点,移动右端点时,区间和不是单调递增的.尺取法的模板题中区间和是单调的,所以找到大于某一值的区间右端点就可以固定.而这道题中因为存在负值,所以区间的变化不是单调的,不能按照普通的方法解决.我们考虑一段区间和,不仅可以认为是a[l]+a[l+1]+...+a[r],还可以看作是sum[r]-sum[l-1],而这道题中要求的区间和的绝对值,就可以看作是max(sum[r],sum[l-1])-min(sum[r],sum[l-1]).这样求一个前缀和,进行排序,双指针确定区间左右端点,这样sum[r]-sum[l](排序后的编号)就代表一个区间和的绝对值,这样的区间就是单调的.如此一来,确定左端点,移动右端点,sum[r]-sum[l]就是单增的,找到sum[r]-sum[l]>t的位置即可停止,然后l++,sum[r-1]-sum[l]比之前的sum[r-1]-sum[l]更小(减数增大),本来就比t小,现在小得更多了,对于任意r'<=r-1都是如此,就不必考虑,从sum[r]-sum[l]开始继续即可.

    注意:

    1.sum[r]-sum[l-1],因为1<=l<=r,所以l-1>=0,注意sum[0].s=0,sum[0].num=0,要参与排序,并且每一次都要重新赋值,因为上一组数据排序后sum[0].s不一定是0,这将影响到语句"sum[i]=point(sum[i-1].s,i)" .

    2.对于区间右端点表示的状态,最好是用来表示当前右端点的位置,然后每一次更新状态时都要判断,与ans比较,看是否更新最优解.

    3.由于r>=l所以r>l-1,也就是说两个区间端点不能是同一个点,当l追上r时,r++.

    4.其实尺取法的写法可以优化,现在的写法是两层循环,l改变后进行一次操作,然后对于同一个l改变r,每次进行一次操作,这样在一个外层循环内部要写两遍操作.其实对于l和r的改变是一样,都是区间状态的改变,所以只要一层循环即可.

      while (l<=n&&r<=n&&ans!=0) 
     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<algorithm>
     4 using std :: sort;
     5 using std :: min;
     6 using std :: max;
     7 
     8 const int maxn=100005,INF=0x7fffffff;
     9 
    10 int a[maxn],s[maxn];
    11 int n,q,t;
    12 
    13 struct point
    14 {
    15     int s,num;
    16     point() {}
    17     point(int a,int b) : s(a) , num(b) {}
    18 }sum[maxn];
    19 
    20 bool comp(point x,point y) { return x.s<y.s; }
    21 
    22 int value(int l,int r) { return abs(sum[r].s-sum[l].s); }
    23 
    24 int L(int l,int r) { return min(sum[l].num,sum[r].num)+1; }
    25 
    26 int R(int l,int r) { return max(sum[l].num,sum[r].num); }
    27 
    28 
    29 void solve(int t)
    30 {
    31     int ans=INF,res,idxl,idxr;
    32     int r=1;
    33     for(int l=0;l<=n;l++)
    34     {    
    35         if(l==r) r++;
    36         if(r>n) break;
    37         int now=value(l,r);
    38         int d=abs(now-t);
    39         if(d<=ans)
    40         {
    41             ans=d;
    42             res=now;
    43             idxl=L(l,r);
    44             idxr=R(l,r);
    45         }
    46         while(r<n&&now<t)
    47         {
    48             r++;
    49             now=value(l,r);
    50             int d=abs(now-t);
    51             if(d<=ans)
    52             {
    53                 ans=d;
    54                 res=now;
    55                 idxl=L(l,r);
    56                 idxr=R(l,r);
    57             }
    58         }
    59         if(now<t) break;        
    60         if(now==t)
    61         {
    62             break;
    63         }
    64     }
    65     printf("%d %d %d
    ",res,idxl,idxr);
    66 }
    67 
    68 void init()
    69 {
    70     while(scanf("%d%d",&n,&q)&&(n!=0||q!=0))
    71     {
    72         sum[0].s=0;
    73         sum[0].num=0;
    74         for(int i=1;i<=n;i++)
    75         {
    76             scanf("%d",&a[i]);
    77             sum[i]=point(sum[i-1].s+a[i],i);
    78         }
    79         sort(sum,sum+n+1,comp);
    80         for(int i=1;i<=q;i++)
    81         {
    82             scanf("%d",&t);
    83             solve(t);
    84         }
    85     }
    86 }
    87 
    88 int main()
    89 {
    90     freopen("bound.in","r",stdin);
    91     freopen("bound.out","w",stdout);
    92     init();
    93     fclose(stdin);
    94     fclose(stdout);
    95     return 0;
    96 }
    View Code
  • 相关阅读:
    JS(原生语法)_实现酷酷的动态简历
    Linux外在设备的使用
    查看系统内存信息
    查看CPU信息
    查看系统PCI设备
    配置网络
    Linux分区
    Observer
    Singleton
    Open closed principle
  • 原文地址:https://www.cnblogs.com/Sunnie69/p/5423228.html
Copyright © 2020-2023  润新知