参考资料:
[1]:http://www.voidcn.com/article/p-huucvank-dv.html
题意:
题意就是找一个连续的子区间,使它的和的绝对值最接近target。
题解:
这题的做法是先预处理出前缀和,然后对前缀和进行排序,再用尺取法贪心的去找最合适的区间。
要注意的是尺取法时首尾指针一定不能相同,因为这时区间相减结果为0,但实际上区间为空,这是不存在的,可能会产生错误的结果。
处理时,把(0,0)这个点也放进数组一起排序,比单独判断起点为1的区间更方便。
还有ans初始化的值INF一定要大于t的最大值。
最后说说这个题最重要的突破口,对前缀和排序。为什么这么做是对的呢?
因为这题是取区间的和的绝对值,所以所以用sum[r]-sum[l] 和 sum[l]-sum[r]是没有区别的。
这样排序后,把原来无序的前缀和变成有序的了,就便于枚举的处理,并且不影响最终结果。
以上分析来自参考资料[1]。
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 #define P pair<int ,int > 6 const int maxn=1e5+10; 7 8 int n,k; 9 P p[maxn]; 10 11 bool cmp(P _a,P _b){ 12 return _a.second < _b.second; 13 } 14 void Solve(int t) 15 { 16 int l=0,r=1; 17 int resL=p[l].first,resR=p[r].first;//先假设区间[1,p[1].first]为解 18 int resSum=p[r].second-p[l].second; 19 while(r <= n) 20 { 21 int curSum=p[r].second-p[l].second; 22 if(abs(curSum-t) < abs(resSum-t))//判断是否可以更新 resSum 23 { 24 resSum=curSum; 25 resL=p[l].first; 26 resR=p[r].first; 27 } 28 if(curSum < t)//如果当前区间值过小,增大当前值 29 r++; 30 else if(curSum > t)//如果当前区间值过大,减小当前值 31 l++; 32 else 33 break; 34 if(l == r) 35 r++; 36 } 37 if(resL > resR) 38 swap(resL,resR); 39 printf("%d %d %d\n",resSum,resL+1,resR);//while()循环中做区间减法时始终左边界一直被减掉 40 } 41 int main() 42 { 43 // freopen("C:\\Users\\lenovo\\Desktop\\in.txt\\poj2566.txt","r",stdin); 44 while(~scanf("%d%d",&n,&k),n != 0 || k != 0) 45 { 46 int sum=0; 47 for(int i=1;i <= n;++i) 48 { 49 int val; 50 scanf("%d",&val); 51 sum += val; 52 p[i]=P(i,sum); 53 } 54 p[0]=P(0,0); 55 sort(p,p+n+1,cmp); 56 while(k--) 57 { 58 int t; 59 scanf("%d",&t); 60 Solve(t); 61 } 62 } 63 return 0; 64 }