poj2566 Bound Found

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

/*
這是一道尺取法的好題, 是深刻反映尺取思想的. 並且邊界條件比較嚴格, 很考驗編碼能力.

尺取法是一種技巧, 主要運用在區間範圍滿足某種性質的問題.
如果一道題是滿足尺取法的, 那麼它是具有某種單調性的.

比如說 poj3061 Subsequence
這道題是求一段連續的區間和是大於等於S的, 最小的區間長度,
因爲所有數字都是正數, 所以當用lo, hi表示一段區間時,
當hi向後移動時, 區間和是上升的,
當lo向後移動時, 區間和是下降的.
也就是說, 當向後移動lo時, 區間和下降, 爲了使區間和上升, 只能向後移動hi.
這就是一個單調性.

再比如說 poj3320 Jessica's Reading Problem
這道題是求一個最小的區間長度, 這個區間有Jessica要複習的所有知識.
用lo, hi表示區間, 初始都爲0, 區間爲空,
首先要不斷遞增hi, 使[lo, hi)這段區間覆蓋了所有的知識點.
接着遞增lo, 直到[lo, hi)這段區間首次沒有覆蓋所有的知識點.
這時候, 只有把hi遞增才能使[lo, hi)覆蓋所有的知識點.
這樣不斷的遞增lo, hi, 也滿足尺取法的單調性.
具體做法可以通過離散化 + 數組模擬

而找到Bound Found和上述兩題是不一樣的, 這道題初看是找不到單調性的, 必須自己構造.
這道題要求的是一段區間, 其區間和的絕對值是最接近t的.
由於數字是有正有負的,
如果用lo, hi表示一段區間的話, 遞增lo, 不一定會使區間和下降,
遞增hi, 不一定會使區間和上升. 這不滿足尺取法要求的單調性.

如何做呢?
一段區間的和, 我們通常可以用前綴和表示.
即[lo, hi] = prefix(hi) - prefix(lo - 1)
這道題要求的是一段連續的區間, 而任意來兩個前綴和相減是可以表示一段連續區間的和的.
這樣我們可以通過對前綴和數組排序, 這樣這個數組就是單調的了.
用lo, hi表示兩個數組索引, 並且 prefix[hi] - prefix[lo]確實是一段區間的和的絕對值
遞增hi, 區間和的絕對值上升,
遞增lo, 區間和的絕對值下降.
這樣就找到了尺取法所要求的單調性了.

關鍵之處就是對前綴和數組排序.
並且細節要求很苛刻, 需要細細體會
*/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 1e5 + 600;
const int infinite = 0x3f3f3f3f;
int a[MAXN];
pair<int, int> prefix[MAXN];
int n, k, t;
int ans, l, r;
void compare(int lo, int hi){
    int sum = lo == hi? -infinite: prefix[hi].first - prefix[lo].first;
    if(abs(sum - t) < abs(ans - t)){
        ans = sum;
        l = lo;
        r = hi;
    }
}
int main(){
    while(scanf("%d%d", &n, &k) && n + k){
        prefix[0] = make_pair(0, 0);// 如果不加0, 是無法用兩個索引表示出一段區間的
        for(int i = 0; i < n; ++i){
            scanf("%d", a + i);
            prefix[i + 1] = make_pair(prefix[i].first + a[i], i + 1);// prefix[i]表示這是a數組前i個數字的和
        }

        sort(prefix, prefix + n + 1);

        while(k--){
            scanf("%d", &t);
            int lo = 0, hi = 0;
            ans = -infinite;
            while(true){
                while((hi < n) && (lo == hi? -infinite: prefix[hi].first - prefix[lo].first) < t){
                    ++hi;
                }// 遞增hi, 使這段區間和的絕對值首次大於 t

                if((lo == hi? -infinite: prefix[hi].first - prefix[lo].first) < t){
                    compare(lo, hi);
                    break;
                }// 如果區間和的絕對值是小於t的話, 就退出
                compare(lo, hi - 1);// 最優解只能在 [lo, hi - 1]
                compare(lo, hi);// 和 [lo, hi]之間了

                while(t <= (lo == hi? -infinite: prefix[hi].first - prefix[lo].first)){
                    ++lo;
                }// 遞增lo, 使區間和的絕對值首次小於 t
                compare(lo - 1, hi);
                compare(lo, hi);
            }
            l = prefix[l].second;
            r = prefix[r].second;
            if(r < l){
                swap(r, l);
            }
            // Output
            printf("%d %d %d\n", ans, l + 1, r);
        }// 上述寫法, 時間複雜度的常數較小, 但是細節處在意過多
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章