POJ2566Bound Found(尺取法好题)

来源:互联网 发布:sql server怎么导入表 编辑:程序博客网 时间:2024/06/10 00:58

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

【题目大意】

给出一个整数列,求一段子序列之和最接近所给出的t。输出该段子序列之和及左右端点。

【思路】

……前缀和比较神奇的想法。一般来说,我们必须要保证数列单调性,才能使用尺取法。

预处理出前i个数的前缀和,和编号i一起放入pair中,然而根据前缀和大小进行排序。由于abs(sum[i]-sum[j])=abs(sum[j]-sum[i]),可以忽视数列前缀和的前后关系。此时,sum[r]-sum[l]有单调性。

因此我们可以先比较当前sum[r]-sum[l]与t的差,并更新答案。

如果当前sum[r]-sum[l]<t,说明和还可以更大,r++。

同理,如果sum[r]-sum[l]>t,说明和还可以更小,l++。

如果sum[r]-sum[l]=t,必定是最小答案。

【注意点】

由于序列不能为空,即l<>r,如果l=r则r++。

我们更新答案的时候左右区间端点为乱序,输出的时候调整一下。

#include<bits/stdc++.h>using namespace std;#define pii pair<int, int>typedef long long ll;const int maxn = 1e5+5;pii sum[maxn];int n, k, t;void init(){    sum[0] = pii(0, 0);    int tmp = 0, x;    for(int i = 1; i <= n; i++)    {        scanf("%d", &x);        tmp += x;        sum[i] = pii(tmp, i);    }    sort(sum, sum+n+1);}void solve(){    scanf("%d", &t);    int l = 0, r = 1, minans = 0X3f3f3f3f, ans, ansl, ansr;    while(r <= n && minans)    {        int delta = sum[r].first - sum[l].first;        if(abs(delta-t) <= minans)        {            minans = abs(delta - t);            ans = delta;            ansl = sum[l].second;            ansr = sum[r].second;        }        if(delta < t)            r++;        if(delta > t)            l++;        if(l == r)            r++;    }    if(ansl > ansr) swap(ansl, ansr);    printf("%d %d %d\n", ans, ansl+1, ansr);}int main(){    while(scanf("%d%d", &n, &k) != EOF)    {        init();        for(int i = 1; i <= k; i++)            solve();    }    return 0;}


原创粉丝点击