【ZOJ 1964】【尺取】Bound Found【暑期 No.3】

题意:

        给定一串数字,并给定一个目标数字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;
}

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/81235419
ZOJ