1. 程式人生 > >POJ 2566 Bound Found(字首和排序 + 尺取法)

POJ 2566 Bound Found(字首和排序 + 尺取法)

題意:對一個長度為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; }