POJ

来源:互联网 发布:免费手机打电话软件 编辑:程序博客网 时间:2024/06/08 00:56

题意:

给定一个序列,包含正负数,给定一个数,找到最接近这个数的一段连续区间的和的绝对值,问这断区间的和是多少,左右下标是多少,,

思路:

求连续区间的和尽量接近这个数,我们可能会想到尺取法,如果序列中不存在负数的话

因为有了负数,尺取的时候我们没法判断怎样移动双指针,完全枚举区间又不现实

我们会想怎么转化成可以利用尺取法的一段序列,这时候是没有方向的,然后可以去想,跟一段区间的和有相关关系的就是前缀和

根据不同的前缀和我们能得到一段区间的和,但是还是没法进行双指针尺取,(尺取虽好,但是需要明确是双指针的移动规则)

最后只能对前缀和数组进行排序,(我一开始也没想到,不要怪脑子不行,慢慢就能想到了)

这样我们就能进行尺取,然后得到跟答案最相近的两个前缀和,注意,这里永远是两个指针对应两个前缀和,相减的到那个最优区间,

跟以往的尺取法代表两个区间之和不同,,这里的双指针起到的作用是排序后的前缀和数组有序性,

使得此时双指针对应的两个前缀和大小关系跟给定的数差别存在大小差异时,我们能确定两个指针指向的前缀和应该怎么移动

上述的指针是假指针,代表两个下标


#include<iostream>#include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<cmath>#include<set>#include<queue>#include<stack>#include<map>#define PI acos(-1.0)#define in freopen("in.txt", "r", stdin)#define out freopen("out.txt", "w", stdout)using namespace std;typedef long long ll;typedef unsigned long long ull;const int maxn = 1e5 + 7, maxd = (1<<18)-1, mod = 1e9 + 7;const int INF = 0x7f7f7f7f;int n, m, x;struct node {    int sum, id;}a[maxn];bool cmp(node a, node b) {    return a.sum < b.sum;}void solve() {    int dx = INF, ans;    int l_ = 0, r_ = 1, t = 0, lid, rid;    while(1) {        t = a[r_].sum - a[l_].sum;        if(abs(t-x) < dx) {            dx = abs(t-x);            ans = t;            lid = a[l_].id, rid = a[r_].id;        }        if(t < x) r_ ++;        //else if(t == x) break;        else l_ ++;        if(l_ == r_) r_ ++;        if(r_ > n) break;    }    cout << ans << " " << min(lid, rid)+1 << " " << max(lid, rid) << endl;}int main() {    while(~scanf("%d %d", &n, &m) && n+m) {        a[0].sum = 0; a[0].id = 0;        for(int i = 1; i <= n; ++i) {            scanf("%d", &x);            a[i].sum = a[i-1].sum + x;            a[i].id = i;        }        sort(a, a+n+1, cmp);        for(int i = 0; i < m; ++i) {            scanf("%d", &x);            solve();        }    }    return 0;}