题意:给你一个大小为N 的数组,和一个 K (k次询问),每次询问输入一个t,在数组中寻找一个连续区间使得区间和的绝对值与t的差值尽量小,输出差值大小和区间的左右端点。
当时认为自己了解一点点尺取法,结果发现自己根本不怎么会
推荐博客:https://blog.csdn.net/consciousman/article/details/52348439
思路:这道题用到了尺取法单调性的性质,如果直接对数组进行尺取的话,不满足约束条件(头尾枚举的序列满足某种单调的性质,这样才能进行尺取的操作,如果当前的尾状态不满足约定条件的话,如果保证头不变,之后的尾状态也肯定不满足题意。)
我们可以求一个前缀和(然后排序),用前缀和进行尺取,我们顺便记录下标,则我们取得则是区间 [i+1,j] 的和,然后在判断条件来约束i,j。
完整代码:
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=exp(1.0);
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
struct node
{
int pre;
int sign;
}know[100005];
bool cmp(node a,node b)
{
return a.pre<b.pre;
}
int main()
{
int x,y,n,t,s;
while(scanf("%d %d",&n,&t)!=EOF,n+t)
{
know[0]= {0,0};
for(int i=1; i<=n; i++)
{
scanf("%d",&x);
know[i]=node{know[i-1].pre+x,i}; ///记录前缀和
}
sort(know,know+1+n,cmp);
while(t--)
{
scanf("%d",&s);
int i=0,j=0,ans=1000000007,maxn=1000000007;
while(i<=n&&j<=n&&maxn)///如果差值0结束。
{
if(i==j)
j++; ///如果i,j相等则表示区间为空
else
{
int sum=know[j].pre-know[i].pre; ///表示区间[i+1,j]的和。
if(maxn>abs(sum-s))
{
ans=sum;
maxn=abs(sum-s);
x=min(know[i].sign,know[j].sign)+1;
y=max(know[i].sign,know[j].sign);
}
if(sum>s)
i++;
if(sum<s)
j++;
}
}
printf("%d %d %d\n",ans,x,y);
}
}
return 0;
}