Poj :2566:Bound Found 尺取法,连续序列求极值

题目大意:

给出N个数,Q个询问 要求你输出Q的询问的结果,询问的内容为,找出一个区间,区间内的数的和的绝对值要最接近所给的数

解题思路:

这种求区间和的问题,比如求一个连续区间和大于 k k k且长度最短,求一个连续区间和小于 k k k且长度最长,求一个连续区间和绝对值最接近 k k k都很适合使用尺取法进行求解。之前写过一篇尺取法的用法,我们要解决的无非下面三个问题
1、 什么情况下能使用尺取法?

2、如何推进区间的端点?

3、如何利用当前区间更新结果值?

4、何时结束区间的枚举?

只要一次解决了这三个问题,那么代码也就呼之欲出了。令 l , r l,r l,r分别为尺取法的左右端点,我们分别来看

  1. 尺取法的使用需要数组满足一定的性质,比如单调性,但是这里即使我们求出前缀和 s u m [ i ] sum[i] sum[i],元素有正有负他也不是单调的。要单调必须排序,但是我们会发现排序之后,由于我们每次取的是前缀和之差的绝对值,所有排序对位置影响不大 a b s ( s u m [ i ] − s u m [ j ] ) = = a b s ( s u m [ j ] − s u m [ i ] ) abs(sum[i] - sum[j]) == abs(sum[j] - sum[i]) abs(sum[i]sum[j])==abs(sum[j]sum[i])。不过此时我们需要额外的存储空间,记录该前缀和的原本位置。

  2. 一般的思路 a b s ( s u m [ i ] − s u m [ j ] ) > Q abs(sum[i] - sum[j])>Q abs(sum[i]sum[j])>Q时我们需要推进左端点,否则推进右端点。为了防止出现 l > = r l>=r l>=r,我们可以再加上 i f ( l = = r ) r + + if(l==r)r++ if(l==r)r++

  3. 对每一组 l , r l,r l,r,如果 a b s ( s u m [ r ] − s u m [ l ] ) abs(sum[r] - sum[l]) abs(sum[r]sum[l])的值更为接近 Q Q Q,那么就可以更新结果值以及记录此时的左右端点。

  4. 最简单的,当 l , r l,r l,r有一个区间端点时就结束枚举,因为如果 r r r先移到端点,说明此时 a b s ( s u m [ r ] − s u m [ l ] ) < Q abs(sum[r] - sum[l])<Q abs(sum[r]sum[l])<Q,再右移 l l l只会使得值更小,差距更大、

#include<iostream>
#include<cmath>
#include<iomanip>
#include<vector>
#include<map>
#include<queue>
#include<algorithm>
#include<string.h>
using namespace std;

#define ll int
#define Max 100050

struct elem {
	ll v, id;
}sum[Max];
bool cmp(elem e1, elem e2) {
	return e1.v < e2.v;
}

ll n, k, e[Max];
int main() {
	while (cin >> n >> k) {
		if (n + k == 0) break; 
		e[0] = 0; sum[0].v = 0; sum[0].id = 0;
		for (ll i = 1; i <= n; i++) { cin >> e[i]; sum[i].v = sum[i - 1].v + e[i]; sum[i].id = i; }
		sort(sum , sum + n + 1, cmp);
		for (ll i = 0; i < k; i++) {
			ll q, l = 0, r = 1; cin >> q;
			ll res = 2000000010, minl = 0, minr = 0, ans = 0;//res:当前最小的误差
			while (l <= n && r <= n) {
				ll val = abs(sum[r].v - sum[l].v), tmp = abs(val - q);
				if (tmp < res) {
					res = tmp; ans = val;
					minl = sum[l].id; minr = sum[r].id;
				}
				if (val > q)l++;
				else if (val < q)r++;
				else break;
				if (l == r) r++;
			}
			if (minl > minr) swap(minl, minr); minl += 1;
			cout << ans << " " << minl << " " << minr << endl;
		} 
	}
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值