【尺取法好題】POJ2566-Bound Found


【題目大意】

給出一個整數列,求一段子序列之和最接近所給出的t。輸出該段子序列之和及左右端點。

【思路】

……前綴和比較神奇的想法。一般來說,我們必須要保證數列單調性,才能使用尺取法。

預處理出前i個數的前綴和,和編號i一起放入pair中,然而根據前綴和大小進行排序。由於abs(sum[i]-sum[j])=abs(sum[j]-sum[i]),可以忽視數列前綴和的前后關系。此時,sum[r]-sum[l]有單調性。

因此我們可以先比較當前sum[r]-sum[l]與t的差,並更新答案。

如果當前sum[r]-sum[l]<t,說明和還可以更大,r++。

同理,如果sum[r]-sum[l]>t,說明和還可以更小,l++。

如果sum[r]-sum[l]=t,必定是最小答案。

【注意點】

由於序列不能為空,即l<>r,如果l=r則r++。

我們更新答案的時候左右區間端點為亂序,輸出的時候調整一下。

就OK了!

*本來想要尺取弄一個合集,然而這道題做的時候想了半天。還是單獨拿出來吧orz

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 const int MAXN=1e5+5;
 8 const int INF=2147483640;
 9 pair<int,int> sum[MAXN];
10 int n,k,t;
11 
12 void init()
13 {
14     sum[0]=make_pair(0,0);
15     int tmp=0;
16     for (int i=1;i<=n;i++) 
17     {
18         int x;
19         scanf("%d",&x);
20         tmp+=x;
21         sum[i]=make_pair(tmp,i);
22     }
23     sort(sum,sum+n+1);     
24 }
25 
26 void solve()
27 {
28     scanf("%d",&t);
29     int l=0,r=1,minans=INF,ans,ansl,ansr;
30     while (r<=n && minans)//這里一開始寫成了ans,以后變量名不要取那么相像orz 
31     {
32         int delta=sum[r].first-sum[l].first;
33         if (abs(delta-t)<=minans)
34         {
35             minans=abs(delta-t);
36             ans=delta;
37             ansl=sum[l].second;
38             ansr=sum[r].second;
39         }
40         if (delta<t) r++; 
41         if (delta>t) l++;
42         if (l==r) r++;//☆注意序列不能為空!
43     }
44     if (ansl>ansr) swap(ansl,ansr);//注意排序后是無序的,左右區間要調整回有序 
45     printf("%d %d %d\n",ans,ansl+1,ansr); 
46 }
47 
48 int main()
49 {
50     while (scanf("%d%d",&n,&k)!=EOF) 
51     {
52         init();
53         for (int i=1;i<=k;i++) solve();
54     }
55     return 0;
56 } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM