POJ_2566_Bound_Found_(尺取法+前缀和)
描述
http://poj.org/problem?id=2566
给出一个整数序列,并给出非负整数t,求数列中连续区间和的绝对值最接近k的区间左右端点以及这个区间和的绝对值.
Time Limit: 5000MS | Memory Limit: 65536K | |||
Total Submissions: 2592 | Accepted: 789 | Special Judge |
Description
You are given the sequence of n integers and the non-negative target t. You are to find a non-empty range of the sequence (i.e. a continuous subsequence) and output its lower index l and its upper index u. The absolute value of the sum of the values of the sequence from the l-th to the u-th element (inclusive) must be at least as close to t as the absolute value of the sum of any other non-empty range.
Input
Output
Sample Input
5 1 -10 -5 0 5 10 3 10 2 -9 8 -7 6 -5 4 -3 2 -1 0 5 11 15 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 15 100 0 0
Sample Output
5 4 4 5 2 8 9 1 1 15 1 15 15 1 15
Source
分析
尺取法.
一眼看过去就是用尺取法找和t绝对值相差最小的区间和,但是这道题里的序列并不是非负的,这意味着固定左端点,移动右端点时,区间和不是单调递增的.尺取法的模板题中区间和是单调的,所以找到大于某一值的区间右端点就可以固定.而这道题中因为存在负值,所以区间的变化不是单调的,不能按照普通的方法解决.我们考虑一段区间和,不仅可以认为是a[l]+a[l+1]+...+a[r],还可以看作是sum[r]-sum[l-1],而这道题中要求的区间和的绝对值,就可以看作是max(sum[r],sum[l-1])-min(sum[r],sum[l-1]).这样求一个前缀和,进行排序,双指针确定区间左右端点,这样sum[r]-sum[l](排序后的编号)就代表一个区间和的绝对值,这样的区间就是单调的.如此一来,确定左端点,移动右端点,sum[r]-sum[l]就是单增的,找到sum[r]-sum[l]>t的位置即可停止,然后l++,sum[r-1]-sum[l]比之前的sum[r-1]-sum[l]更小(减数增大),本来就比t小,现在小得更多了,对于任意r'<=r-1都是如此,就不必考虑,从sum[r]-sum[l]开始继续即可.
注意:
1.sum[r]-sum[l-1],因为1<=l<=r,所以l-1>=0,注意sum[0].s=0,sum[0].num=0,要参与排序,并且每一次都要重新赋值,因为上一组数据排序后sum[0].s不一定是0,这将影响到语句"sum[i]=point(sum[i-1].s,i)" .
2.对于区间右端点表示的状态,最好是用来表示当前右端点的位置,然后每一次更新状态时都要判断,与ans比较,看是否更新最优解.
3.由于r>=l所以r>l-1,也就是说两个区间端点不能是同一个点,当l追上r时,r++.
4.其实尺取法的写法可以优化,现在的写法是两层循环,l改变后进行一次操作,然后对于同一个l改变r,每次进行一次操作,这样在一个外层循环内部要写两遍操作.其实对于l和r的改变是一样,都是区间状态的改变,所以只要一层循环即可.
即
while (l<=n&&r<=n&&ans!=0)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 using std :: sort; 5 using std :: min; 6 using std :: max; 7 8 const int maxn=100005,INF=0x7fffffff; 9 10 int a[maxn],s[maxn]; 11 int n,q,t; 12 13 struct point 14 { 15 int s,num; 16 point() {} 17 point(int a,int b) : s(a) , num(b) {} 18 }sum[maxn]; 19 20 bool comp(point x,point y) { return x.s<y.s; } 21 22 int value(int l,int r) { return abs(sum[r].s-sum[l].s); } 23 24 int L(int l,int r) { return min(sum[l].num,sum[r].num)+1; } 25 26 int R(int l,int r) { return max(sum[l].num,sum[r].num); } 27 28 29 void solve(int t) 30 { 31 int ans=INF,res,idxl,idxr; 32 int r=1; 33 for(int l=0;l<=n;l++) 34 { 35 if(l==r) r++; 36 if(r>n) break; 37 int now=value(l,r); 38 int d=abs(now-t); 39 if(d<=ans) 40 { 41 ans=d; 42 res=now; 43 idxl=L(l,r); 44 idxr=R(l,r); 45 } 46 while(r<n&&now<t) 47 { 48 r++; 49 now=value(l,r); 50 int d=abs(now-t); 51 if(d<=ans) 52 { 53 ans=d; 54 res=now; 55 idxl=L(l,r); 56 idxr=R(l,r); 57 } 58 } 59 if(now<t) break; 60 if(now==t) 61 { 62 break; 63 } 64 } 65 printf("%d %d %d\n",res,idxl,idxr); 66 } 67 68 void init() 69 { 70 while(scanf("%d%d",&n,&q)&&(n!=0||q!=0)) 71 { 72 sum[0].s=0; 73 sum[0].num=0; 74 for(int i=1;i<=n;i++) 75 { 76 scanf("%d",&a[i]); 77 sum[i]=point(sum[i-1].s+a[i],i); 78 } 79 sort(sum,sum+n+1,comp); 80 for(int i=1;i<=q;i++) 81 { 82 scanf("%d",&t); 83 solve(t); 84 } 85 } 86 } 87 88 int main() 89 { 90 freopen("bound.in","r",stdin); 91 freopen("bound.out","w",stdout); 92 init(); 93 fclose(stdin); 94 fclose(stdout); 95 return 0; 96 }