您的位置:首页 > 其它

POJ 2566 Bound Found(前缀和排序 + 尺取法)

2015-07-27 14:10 316 查看
题目链接:http://poj.org/problem?id=2566

题意:对一个长度为n的数列,做k次查询,每次查询一个数t,求原数列中的一个子区间[l, r],使得该子区间的和的绝对值最接近t。

思路:在原数列开头添加一个0,处理好现数列a
的前缀和pre
。则原问题转化为在前缀数组中求2个数pre[i],pre[j]的差的绝对值最接近t的。对于每次找到的2个下标分别为i和j的2个数,所对应a的区间为[min(i, j) + 1, max(i, j)]。

那么将前缀和数组排序后,即可用尺取法得到最接近的值。

注意的是:排序时需要保留原来的下标值,以便于求区间。

代码如下

#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;

const int N = 1e5 + 10;
const int INF = 0x7fffffff;

struct Presum {
    int sum;
    int id;
};

int n, k;
int a
;
Presum pre
;

bool cmp(Presum a, Presum b) {
    return a.sum < b.sum;
}

int main() {

    while (scanf("%d%d", &n, &k) != EOF && n && k) {
        pre[0].sum = 0;
        pre[0].id = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            pre[i].sum = pre[i - 1].sum + a[i];
            pre[i].id = i;
        }
        sort(pre, pre + n + 1, cmp);

        for (int i_q = 0; i_q < k; i_q++) {
            int t;
            scanf("%d", &t);
            int ansl, ansr, ans;
            int l = 0, r = 1;
            int Min = INF;
            while (r <= n) {
                int sub = pre[r].sum - pre[l].sum;
                if (abs(sub - t) < Min) {
                    Min = abs(sub - t);
                    ansl = min(pre[l].id, pre[r].id) + 1;
                    ansr = max(pre[l].id, pre[r].id);
                    ans = sub;
                }
                if (sub < t)
                    r++;
                else if (sub > t)
                    l++;
                else break;
                if (l == r)
                    r++;
            }
            printf("%d %d %d\n", ans, ansl, ansr);
        }
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: