poj 2566 尺取法

来源:互联网 发布:淘宝双十一海报尺寸 编辑:程序博客网 时间:2024/04/28 08:34

题意:

给一个大小为n(1e5)的数组,然后给k个数t 。

要求在这个数组中连续一段的和的绝对值最接近数t的那一段的起点fr,终点to,和这一段和的绝对值ansSum 。


解析:

敲了一个晚上+早上,蛋疼。。。

这题的解法很不容易想到。

首先,尺取法适用的条件:

1.连续的序列;

2.序列满足单调性。


这题明显不能满足第2个条件。

所以,这题的做法是将n个数的前缀和记录下来,然后将前缀和排序,用这种方法来使其满足单调性。

参照大牛做法。

http://www.hankcs.com/program/algorithm/poj-2566-bound-found.html


代码:

#include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <climits>#include <cassert>#define LL long longusing namespace std;const int inf = 0x3f3f3f3f;const double eps = 1e-8;const double pi = 4 * atan(1.0);const double ee = exp(1.0);const int maxn = 100000 + 10;int n, k;pair<int, int> ac[maxn]; //sum <-> indexint getSum(int& ansSum, int l, int r, int t, int& fr, int& to){    if (r <= l)        return -inf;    int sum = ac[r].first - ac[l].first;    if (abs(t - sum) < abs(t - ansSum))    {        ansSum = sum;        fr = min(ac[l].second, ac[r].second);        to = max(ac[l].second, ac[r].second);    }    return sum;}void solve(int t){    int l = 0, r = 0;    int sum = 3000000000;    int fr, to;    int ansSum = 3000000000;    while (1)    {        while (r < n && sum < t)            sum = getSum(ansSum, l, ++r, t, fr, to);        if (sum < t)            break;        sum = getSum(ansSum, ++l, r, t, fr, to);    }    printf("%d %d %d\n", ansSum, fr + 1, to);}int main(){#ifdef LOCAL    freopen("in.txt", "r", stdin);#endif // LOCAL    while (~scanf("%d%d", &n, &k))    {        if (!n && !k)            break;        ac[0] = make_pair(0, 0);        for (int i = 1; i <= n; i++)        {            int t;            scanf("%d", &t);            ac[i] = make_pair(ac[i - 1].first + t, i);        }        sort(ac, ac + n + 1);        while (k--)        {            int t;            scanf("%d", &t);            solve(t);        }    }    return 0;}


0 0