题目链接: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;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/u014357885/article/details/47084279