题意:
给定一串数字,并给定一个目标数字t,求出一个区间,该区间上的所有值相加后取绝对值最接近t.
分析:
先考虑能不能用尺取来做,如果用尺取来做,那么尺取的数列必须是单调的,否则无法确定何时左右端点移动,因此考虑能不能用前缀和来记录每一个点.
因此考虑用sum[i]表示 1-i 的前缀和,并将sum[i]这个数列进行升序,因此sum[r]-sum[l]则表示一个区间内的累加和,但是是左开右闭的一个区间.
由此 sum[r]-sum[l] < t,r++;sum[r]-sum[l] > t,l++;进行尺取即可.
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int n,k;
int a[maxn];
ll t,ans;
struct Sum{
ll v,num;
}sum[maxn];
bool cmp(Sum x,Sum y)
{
return x.v < y.v;
}
ll abss(ll x)
{
if(x < 0) return (-1)*x;
else return x;
}
int main()
{
while(scanf("%d%d",&n,&k),n+k)
{
sum[0].v = 0; sum[0].num = 0;
rep(i,1,n)
{
scanf("%d",&a[i]);
sum[i].v = sum[i-1].v+a[i];
sum[i].num = i;
}
sort(sum,sum+1+n,cmp);
int lp,rp;
while(k--)
{
scanf("%lld",&t);
ans = 1e17;
int tmp = 0;
int l = 0,r = 1;
while(l<r && r<=n)
{
tmp = abss(sum[r].v-sum[l].v);
if(abss(tmp-t) < abss(ans-t))
{
ans = tmp;
lp = sum[l].num; rp = sum[r].num;
}
if(tmp < t)
r++;
else if(tmp > t) l++;
else break;
if(l == r) r++;
}
if(lp > rp) swap(lp,rp);
printf("%lld %d %d\n",ans,lp+1,rp);
}
}
return 0;
}