题目链接Bound Found
题意
有N个整数数,可负可正。给一个数t,然后从这N个数中找一个连续的区间,使得这个连续区间和的绝对值最接近t,求出这个连续区间和的绝对值和以及区间的上下界。
思路
- 尺取法
顾名思义,像尺子一样取一段,尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。 - 什么时候可以使用尺取法?
我自己的理解是:答案在一段连续的区间内或者是一段连续的区间(可能有多个区间满足情况,题目要求你求出一个最优的区间),一般区间都是从数组的左端动态的移向右端。随着区间范围的扩大(右端点右移),就越靠近答案,区间缩小(左端点右移),就越远离答案。并且能够根据当前区间的状态判断是左端点或是右端点右移。 - 例题
像这样反复地推进区间的开头和末尾,来求取满足条件的最小区间的方法被称为尺取法。 - 对于本题
因为数组中的数有正有负,不能使用尺取法。为了可以使用尺取法,同时为了求出最优区间的上下界,可以先求出来前缀和,然后按照前缀和进行大小排序,排序的时候将前缀和以及下标使用结构体存储起来进行结构体排序。如果区间和sum<t,将右端点右移:如果sum>t,将左端点右移,这样保证sum尽可能的靠近t,区间在进行动态变换的时候更新答案即可
代码
//#pragma GCC optimize(2)
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
using namespace std;
typedef long long ll;
typedef unsigned long ul;
typedef unsigned long long ull;
#define pi acos(-1.0)
#define e exp(1.0)
#define pb push_back
#define mk make_pair
#define fir first
#define sec second
#define scf scanf
#define prf printf
typedef pair<int,int> pa;
const int INF=0x3f3f3f3f;
const int MAX_N=1e5+7;
int N,K,T;
int num[MAX_N];
struct node{
int s,pos;
bool operator<(const node &a)const{
//从小到大排序
if(s!=a.s)
return s<a.s;
return pos<a.pos;
}
};
vector<node>V;
void do_(int t){
int L=0,R=1,aL,aR,minn=INF,sum=0,ans;
while(L<=R&&R<V.size()){
int sum=V[R].s-V[L].s;
if(abs(sum-t)<minn){
minn=abs(sum-t);
ans=sum;
aR=max(V[R].pos,V[L].pos);
aL=min(V[R].pos,V[L].pos);
}
if(sum<t)
R++;
else if(sum>t)
L++;
else
break;
if(L==R){
R++;
}
}
prf("%d %d %d\n",ans,aL+1,aR);
}
int main()
{
// freopen(".../.txt","w",stdout);
// freopen(".../.txt","r",stdin);
// ios::sync_with_stdio(false);
int i,j,k;
while(~scf("%d %d",&N,&K)&&(N||K)){
V.clear();
for(i=1;i<=N;i++){
scf("%d",&num[i]);
}
int tmp=0;
V.pb({
0,0});
for(i=1;i<=N;i++){
tmp=tmp+num[i];
V.pb({
tmp,i});
}
sort(V.begin(),V.end());
for(i=0;i<K;i++){
scf("%d",&T);
do_(T);
}
}
return 0;
}