POJ 2566-Bound Found(变形尺取法)

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

题意:给你n个整数,然后是k次查询,每次都查询一个绝对值和接近t的子序列,并且输出所求得的和与上、下界

分析:

            这一道题很像是尺取法的板子,但是仔细去想,尺取法是应用于单调递增的前序和的序列才行,什么意思呢?

           1,2,-3,4               像这个序列,它的前序和是1 ,3,0,4  这个就不是递增的序列和;

扫描二维码关注公众号,回复: 5741860 查看本文章

           但是像下面这个:

          -3 ,1,2,3                它的前序和是-3,-2,0,3  ,这个就是一个递增的序列和;

          那么依据这个原则,我们需要去改变这个所给的序列,怎么去把这个序列变成一个递增的

         我们可以这样去处理,举个栗子:

         -9 8 -7 6 -5 4 -3 2 -1 0

对于上面这个样例(也就是题目中的第二个样例),它的前序和是:

         -9,-1,-8,-2,-7,-3,-6,-4,-5,-5

下标  1, 2,  3,   4,  5, 6,   7,  8,  9, 10

然后将这些值按照值从小到大进行排序:

         -9,-8,-7,-6,-5,-5,-4,-3,-2,-1

下标:1,3,5,  7,  9,10,8,  6, 4, 2

通过前序和(也叫前缀和吧),就能够求出任意区间的和,比如sum=p[i]-p[j],这就是区间[j+1,i]的区间和,处理到这里就可以看出,剩下的就是用尺取法了,只需要每次找到合适的区间和-t,将其值最小化就可以了

因为是求得的是绝对值,因此不需要在乎谁的下标大,谁的下标小,只需要在后面进行处理一下就好了

下面是AC的代码:

#include <iostream>
#include <algorithm>
#include <string>
#include <map>
#include <cstring>
//#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <set>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Abs(x) ((x)>=0?(x):-(x))
int n,k;
ll t;
pair<int,int>p[N];
void solve(){
    int i=0,j=1;
    int pare=INF;
    ll ans=LINF,sum=0;
    int l,r;
    while(j<=n&&i<=n){
        sum=p[j].first-p[i].first;
        if(Abs(sum-t)<pare){
            ans=sum;
            pare=Abs(sum-t);
            r=p[j].second;
            l=p[i].second;
        }
        if(sum<t) j++;
        else if(sum>t) i++;
        else break;
        if(i==j) j++;
    }
    if(r<=l) swap(l,r);
    printf("%lld %d %d\n",ans,l+1,r);
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    while(scanf("%d%d",&n,&k)!=EOF,n+k){
        int tt=0;
        p[0]=make_pair(0,0);
        rep(i,1,n){int u;
        scanf("%d",&u);
          tt+=u;
        p[i]=make_pair(tt,i);
        }
//        cout<<p[1].first<<" "<<p[1].second<<endl;
        sort(p,p+n+1);
        while(k--){
            scanf("%lld",&t);
            solve();
        }
    }
     return 0;
 }

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/88951213