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

来源:互联网 发布:返利网登录不了淘宝 编辑:程序博客网 时间:2024/05/16 18:04

题目链接:http://poj.org/problem?id=2566

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

思路:在原数列开头添加一个0,处理好现数列a[N]的前缀和pre[N]。则原问题转化为在前缀数组中求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[N];Presum pre[N];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;}
0 0
原创粉丝点击