POJ-2566 Bound Found
Bound Found
给出一个数组,要找到一个连续子序列的和的绝对值最接近给出的k
尺取
这个尺取非常难想到
我们使用尺取的时候讲究的是固定了左端,然后右端一直往右滑动,如果滑动到一个不符合条件的情况,那么后面的情况都不符合。但是由于该数组存在负数的情况,所以显然不符合尺取的条件,考虑排序
直接排序显然做不了,我们考虑对每个位置求一个前缀和,然后带着index进行排序,这样就可以做到滑动的时候,区间的差是不断增大的(这里的区间[l,r]的index并不单调,所以有点反直觉,有点难想到)
做这题有感悟:
-
尺取真的得小心边界
-
在考虑前缀和的时候,一定要考虑一开始的sum[0],这里也是要加入一起排序中
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10;
typedef long long ll;
struct node
{
ll val, id;
node(){}
node(ll _val, ll _id){val = _val; id = _id;}
}num[maxn];
bool cmp(const node& a, const node& b)
{
return a.val < b.val;
}
ll sabs(ll a)
{
return a > 0 ? a : -a;
}
int main()
{
int n, m;
while(scanf("%d%d", &n, &m) != EOF && (n | m))
{
num[0].val = num[0].id = 0;
for(int i=1; i<=n; i++)
{
ll x;
scanf("%lld", &x);
x += num[i-1].val;
num[i] = node(x, i);
}
sort(num, num + n + 1, cmp);
while(m--)
{
ll k;
scanf("%lld", &k);
int l = 0, r = 1, lans = 0, rans = 0;
ll minn = 1e17 + 10, ans = 0;
while(r <= n)
{
ll now = num[r].val - num[l].val;
ll temp = abs(now - k);
if(temp < minn)
{
lans = num[r].id;
rans = num[l].id;
minn = temp;
ans = now;
}
if(now == k) break;
if(now > k) l++;
else r++;
if(l == r) r++;
}
if(lans > rans) swap(lans, rans);
printf("%lld %d %d\n", ans, lans + 1, rans);
}
}
return 0;
}