题目链解:https://www.luogu.org/problemnew/show/P1631
这题刚看完有点蒙,两个for循环的话不但爆空间,也爆时间啊!
看了大佬解释后,发现有2种做法,二分答案或优先队列(堆)。个人倾向于二分答案。
想到二分答案并不容易,有3点关键:
1.题目给的是有序的!
2.从小达到排,找到最大的停止!(最大值!关键二分)
3.这是一个范围问题,最小范围和最大范围上限已知!(明显二分!)
(选n个数,也就是n次,和移动石头m一样的。。)
另外需要注意的是,第一个for循环枚举答案的时候,完全可以找到一个点跳出达到很大的剪枝优化!(后面肯定不会有更小的了就退出完成)
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 const int maxn=1e6; 5 int a[maxn],b[maxn]; 6 int c[maxn];//结果数组要开大,因为可能有重复 7 int n; 8 9 int judge(int x) 10 { 11 int cnt=0; 12 for(int i=1;i<=n;i++) 13 { 14 if(cnt>n) break;//数选够了退出 15 if(a[i]+b[1]>x) break;//这个优化很重要(整个循环退出),不用枚举到底了,如果第i个数+b第一个都比x大后面(包括下一轮)肯定所有更大! 16 //这个>和j循环里的>意义不同更重大,j循环里的>退出,下一轮可能还有更小的!但第一个>退出就代表下一轮也不可能更小了! 17 for(int j=1;j<=n;j++) 18 { 19 if(a[i]+b[j]<x)//选了这个数,数的个数+1 20 { 21 cnt++; 22 if(cnt>n) break; 23 } 24 else if(a[i]+b[j]>x) break;//>这一轮退出(因为下一轮可能有更小的) 25 } 26 } 27 28 return cnt; 29 } 30 31 int main() 32 { 33 ios::sync_with_stdio(false); cin.tie(0); 34 35 cin>>n; 36 for(int i=1;i<=n;++i) cin>>a[i]; 37 for(int i=1;i<=n;++i) cin>>b[i]; 38 39 int l=a[1]+b[1],r=a[n]+b[n]; 40 int ans=0; 41 while(l<=r) 42 { 43 int mid=(l+r)/2; 44 45 int t=judge(mid); 46 if(t<=n) 47 { 48 l=mid+1; 49 ans=mid; 50 } 51 else r=mid-1; 52 } 53 54 int p=0; 55 for(int i=1;i<=n;++i) 56 { 57 if(a[i]+b[1]>ans) break;//和judge里优化一样不再重述 58 for(int j=1;j<=n;++j) 59 { 60 if(a[i]+b[j]<=ans) 61 { 62 c[++p]=a[i]+b[j]; 63 } 64 else break; 65 } 66 } 67 sort(c+1,c+1+p); 68 69 for(int i=1;i<=n-1;i++) cout<<c[i]<<" "; 70 cout<<c[n]<<endl; 71 72 return 0; 73 }
完。
原文:https://www.cnblogs.com/redblackk/p/9933431.html