zoukankan      html  css  js  c++  java
  • POJ 2566 Bound Found(尺取法,前缀和)

    Bound Found
    Time Limit: 5000MS   Memory Limit: 65536K
    Total Submissions: 5207   Accepted: 1667   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

     

    题意:给n和k,输入n个数和k个t,求一个连续子序列使得这个连续子序列的和最接近t,输出这个子序列的和和它的左右端点

    解题思路:尺取法.要使用尺取法必须要保证数列的单调性,而这道题输入的n个数并不是单调的,然后借鉴了大佬的博客...这里用到了前缀和,预处理出前i个数的前缀和,和编号i一起放入pair中,然后根据前缀和从小到大排序,此时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时,r++。更新答案的时候左右区间端点为乱序,输出的时候交换一下即可

    因为前缀和不单调,所以需要先排序。在原数组开头添加0,求出前缀数组。题目即转化为在前缀数组中找pre[i],pre[j],两者之差最接近t,。对于每次找到的2个下标分别为i和j的2个数,所对应a的区间为[min(i, j) + 1, max(i, j)]。

    那么前缀数组排序后,尺取法便可以求得最接近t的值。

     1 #include<cstdio>  
     2 #include<cstring>  
     3 #include<iostream>  
     4 #include<algorithm>  
     5 #define debu  
     6 using namespace std;
     7 const int maxn = 1e5 + 50;
     8 const int INF = 0x3f3f3f3f;
     9 struct Node
    10 {
    11     int id, tot;
    12 };
    13 Node a[maxn];
    14 int n, q, ans, ansl, ansr;
    15 int cmp(Node a, Node b)
    16 {
    17     if (a.tot == b.tot) return a.id<b.id;
    18     else return a.tot<b.tot;
    19 }
    20 int main()
    21 {
    22     while (scanf("%d%d", &n, &q) != EOF && (n + q))
    23     {
    24         a[0].id = 0, a[0].tot = 0;
    25         for (int i = 1; i <= n; i++)
    26         {
    27             int x;
    28             scanf("%d", &x);
    29             a[i].id = i;
    30             a[i].tot = a[i - 1].tot + x;
    31         }
    32         sort(a, a + n + 1, cmp);
    33         for (int i = 0; i<q; i++)
    34         {
    35             int t;
    36             scanf("%d", &t);
    37             int l = 0, r = 1, minx = INF;
    38             while (l <= r && r <= n)
    39             {
    40                 int tmp = a[r].tot - a[l].tot;
    41                 if (abs(tmp - t)<minx)
    42                 {
    43                     minx = abs(tmp - t);
    44                     ans = tmp;
    45                     ansl = a[l].id;
    46                     ansr = a[r].id;
    47                 }
    48                 if (tmp<t) r++;
    49                 else if (tmp>t) l++;
    50                 else break;
    51                 if (l == r) r++;
    52             }
    53             if (ansl>ansr) swap(ansl, ansr);
    54             printf("%d %d %d
    ", ans, ansl + 1, ansr);
    55         }
    56     }
    57     return 0;
    58 }
  • 相关阅读:
    3
    2
    1
    11
    12
    8888888888
    99999999999
    88888888888
    77777777
    10.23
  • 原文地址:https://www.cnblogs.com/caiyishuai/p/8457823.html
Copyright © 2011-2022 走看看