POJ 2566 Bound Found(字首和排序 + 尺取法)
阿新 • • 發佈:2018-12-24
題意:對一個長度為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;
}