题意:求所给的一串数的一个区间使得这个区间内的所有的数相加后的绝对值和所给的数之间的差距尽量小
刚开始看的时候啥思路都没有,最后想到了前缀和,由于这题要输出区间,所以开一个结构体把区间和与其相应位置储存起来,之后按照区间和从小到大排序
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int a[100100],n,k;
struct node
{
int sum;
int id;
}pre[100100];
bool cmp(node a,node b) //尺取法是一种高效的枚举区间的方法,关键要找到一个单调序列,
//这样right,left的变化才有递进性
{
return a.sum<b.sum; //按sum从小到大排序,通过right的增加,差变大,也即区间内的和变大
}
int main()
{
int i,ans_left,ans_right,ans,sub,t;
while(scanf("%d%d",&n,&k),n||k)
{
pre[0].sum=0;
pre[0].id=0;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pre[i].id=i;
pre[i].sum=pre[i-1].sum+a[i];
}
sort(pre,pre+n+1,cmp);
while(k--)
{
scanf("%d",&t);
int left=0,right=1;
int res=0x3f3f3f3f;
while(right<=n)
{
sub=pre[right].sum-pre[left].sum; //利用和的差求出某区间内数的和。
if(abs(sub-t)<res) //不断更新最趋近的区间
{
res=abs(sub-t);
ans_left=pre[left].id;
ans_right=pre[right].id;
ans=sub;
}
if(sub<t)
right++; // right越大,sum差越大,两区间内的数和越大,left越大,sum差越小,两区间内的数和越小
else if(sub>t)
left++;
else //sub与t相等已是最佳区间,可以停止查找了
break;
if(left==right) //尺取法的一种固定操作。
right++;
}
if(ans_left>ans_right)
swap(ans_left,ans_right); //left是绝对不会小于right的,但ans_left可能大于right.(因为这里是用绝对值的)
printf("%d %d %d\n",ans,ans_left+1,ans_right); //这里ans_left+1,因为差的区间是不包过开头的,所以加1
}
}
return 0;
}