poj 2566 Bound Found 尺取法 变形

时间:2022-10-13 12:26:54
Bound Found
Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 2277   Accepted: 703   Special Judge

Description

Signals of most probably extra-terrestrial origin have been received and digitalized by The Aeronautic and Space Administration (that must be going through a defiant phase: "But I want to use feet, not meters!"). Each signal seems to come in two parts: a sequence of n integer values and a non-negative integer t. We'll not go into details, but researchers found out that a signal encodes two integer values. These can be found as the lower and upper bound of a subrange of the sequence whose absolute value of its sum is closest to t.

You are given the sequence of n integers and the non-negative target t. You are to find a non-empty range of the sequence (i.e. a continuous subsequence) and output its lower index l and its upper index u. The absolute value of the sum of the values of the sequence from the l-th to the u-th element (inclusive) must be at least as close to t as the absolute value of the sum of any other non-empty range.

Input

The input file contains several test cases. Each test case starts with two numbers n and k. Input is terminated by n=k=0. Otherwise, 1<=n<=100000 and there follow n integers with absolute values <=10000 which constitute the sequence. Then follow k queries for this sequence. Each query is a target t with 0<=t<=1000000000.

Output

For each query output 3 numbers on a line: some closest absolute sum and the lower and upper indices of some range where this absolute sum is achieved. Possible indices start with 1 and go up to n.

Sample Input

5 1
-10 -5 0 5 10
3
10 2
-9 8 -7 6 -5 4 -3 2 -1 0
5 11
15 2
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
15 100
0 0

Sample Output

5 4 4
5 2 8
9 1 1
15 1 15
15 1 15

Source

题意:给你n个数字,这些数字可正可负,再给你个数字t,求在这个数列中一个连续的子序列,和的绝对值与t相差最小
 
错因分析:刚开始看到这道题目觉得很变态,尺取法怎么可能做得出来,这些数字是可正可负的,,后来看了题解,才恍然大悟,正因为是可正可负所以不能直接用尺取法去解题(尺取法必须是找到单调性),需要进行转化,找到单调性,但是a[i]数组是不能改变的,因为要求连续,那么就自然想到对a[i]求前缀和
 
先贴上别人好的代码,核心思想:对sum进行从小到大的排序,注意需要加入<0,0>这点(为了求得单独的sum,就是从第一个节点到sum[i]对应的i节点),然后尺取法设置左右两个端点,直接在sum数组上进行尺取法,需要注意的是对两个端点移动的判断,假设得出的int temp=p[r].first - p[l].first<k,说明temp偏小,那么r端点右移,否则>k的话,说明temp偏大,需要l端点右移,但是需要注意不能出现l==r的情况,因为序列不能为空,l==r,则序列为空;还有一点很重要的是因为求得是和的绝对值,即|sum[j]-sum[i]|,那么只需要对sum数组进行从小到大排序,用大的减去小的就可以了(这也是去绝对值的体现)
#include<cstdio>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include<map>
#include <algorithm>
#include <set>
using namespace std;
#define MM(a) memset(a,0,sizeof(a))
typedef long long LL;
typedef unsigned long long ULL;
const int mod = 1000000007;
const double eps = 1e-10;
const int inf = 0x3f3f3f3f;
pair<int, int > p[100005];
int n, m, k;
void solve(int k)
{
int l = 0, r = 1, al, ar, av, minn = inf;
while (l<=n&&r<=n&&minn!=0)
{
int temp=p[r].first - p[l].first;
if (abs(temp - k) < minn)
{
minn = abs(temp - k);
ar = p[r].second;
al = p[l].second;
av = temp;
}
if (temp> k)
l++;
else if (temp < k)
r++;
else
break;
if (r == l)
r++;
}
if(al>ar)
swap(al,ar);//因为al和ar大小没有必然关系()取绝对值,所以//要交换
printf("%d %d %d\n", av, al+1, ar);
}
int main()
{
while (~scanf("%d %d", &n, &m))
{
if (!n&&!m) return 0;
p[0] = make_pair(0, 0);
for (int i = 1; i <= n; i++)
{
scanf("%d", &p[i].first);
p[i].first += p[i - 1].first;
p[i].second = i;
}
sort(p, p + n + 1);
while (m--)
{
scanf("%d", &k);
solve(k);
}
}
return 0;
}

  下面是自己的wa代码

好好找茬

#include<cstdio>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include<map>
#include <algorithm>
#include <set>
using namespace std;
#define MM(a) memset(a,0,sizeof(a))
typedef long long LL;
typedef unsigned long long ULL;
const int mod = 1000000007;
const double eps = 1e-10;
const int inf = 0x3f3f3f3f;
pair<int, int > p[100005];
int n, m, k;
void solve(int k)
{
int l = 0, r = 1, al, ar, av, minn = inf;
while (l<=n&&r<=n)
{
int temp = p[r].first - p[l].first;
if (abs(temp - k) < minn)
{
minn = abs(temp - k);
ar = p[r].second;
al = p[l].second;
av = temp;
}
if (temp > k)
l++;
else if (temp < k)
r++;
else
break;
if (r == l)
r++;
}
printf("%d %d %d\n", av, al+1, ar);
}
int main()
{
while (~scanf("%d %d", &n, &m))
{
if (!n&&!m) return 0;
p[0] = make_pair(0, 0);
for (int i = 1; i <= n; i++)
{
scanf("%d", &p[i].first);
p[i].first += p[i - 1].first;
p[i].second = i;
}
sort(p + 1, p + n + 1);
while (m--)
{
scanf("%d", &k);
solve(k);
}
}
return 0;
}