二分搜索作用:降低时间复杂度到log(n);求满足条件的最大的最小值,或是最小最大值;
设计一个bool judge 函数,判断该点是否合法(满足条件)
A题:
还记得我们新生赛上的这题Averyboy的筷子这题吗?众所周知,Averyboy是一个非常的男孩,既然是一个非常的男孩,那么他就会有许多奇葩的爱好。比如收藏筷子。现在美美旸有n双筷子,编号为1-n,同一双筷子编号相同,美美旸把这些筷子放成一排。现在美美旸对av-boy说,如果你能解决我给你出的一个问题,这些筷子都归你。美美旸的问题是:把这些筷子通过一些操作,使得最后这些筷子是同一双的一定相连。每次的操作是,你可以从这2n支筷子中任意取出一支,剩下的筷子自动合并在一起,然后把这支筷子插入任意一个位置,包括两端。(操作次数无限制。)为了刁难av-boy,美美旸故意定了一个条件,每次只能取出编号大于等于x的筷子。现在你的问题是找出最大的x(1 <= x <= n),使得av-boy一定能顺利拿到筷子。
这道题的意思就是说选择一个编号为x的筷子,编号大于等于x的筷子都可以随意移动,那就相当于把编号大于等于x的筷子都删掉,看剩下的筷子编号相同的是不是都连在一起就行,因为剩下的筷子是移动不了的,只能自动合并。由此我们设计出bool judge函数
接下来二分搜索最大的x即可,注意筷子是2*n个
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+10; 4 int a[maxn]; 5 int b[maxn]; 6 int k,n; 7 bool judge(int k) 8 { 9 int i; 10 int cnt=1; 11 for(i=1;i<=2*n;i++) 12 { 13 if(a[i]<k) 14 { 15 b[cnt++]=a[i]; 16 } 17 } 18 cnt--; 19 for(i=1;i<=cnt-1;i++) 20 { 21 if(i&1) 22 { 23 if(b[i+1]!=b[i])return false; 24 } 25 } 26 return true; 27 } 28 int bisearch() 29 { 30 int l=1,r=n; 31 int ans=0; 32 while(l<=r) 33 { 34 int mid=(l+r)>>1; 35 if(judge(mid)) 36 { 37 ans=mid; 38 l=mid+1; 39 } 40 else r=mid-1; 41 } 42 return ans; 43 } 44 int main() 45 { 46 int t; 47 cin>>t; 48 while(t--) 49 { 50 scanf("%d",&n); 51 for(int i=1;i<=2*n;i++)scanf("%d",&a[i]); 52 int ans=bisearch(); 53 printf("%d\n",ans); 54 } 55 return 0; 56 }
B题:
现在有N种物品,每一种物品有一个基础价值a[i],当买k件物品时,第i件物品的最终价值为a[i] + k * i,现在给你S元,你最多能买多少件物品.
最原始的问题,确定一个k,则每件物品的最终价值都被确定;
设计bool judge 函数,对物品的最终价值进行排序,选择最小的k件求和,若和>s,则不合法;
二分查找最大的k;注意如果i*j的乘积很大,则i,j都要开long long ,不然会爆
#include <bits/stdc++.h> using namespace std; const int maxn=1e5+10; long long a[maxn]; long long b[maxn]; long long n; long long s; bool judge(long long k) { int i; for(i=1;i<=n;i++) { b[i]=a[i]+i*k; } sort(b+1,b+n+1); long long sum=0; for(i=1;i<=k;i++) { sum+=b[i]; } if(sum<=s)return true; else return false; } long long bisearch() { int l=1,r=n; long long ans=0; while(l<=r) { long long mid=(l+r)>>1; if(judge(mid)) { ans=mid; l=mid+1; } else r=mid-1; } return ans; } int main() { int t; cin>>t; while(t--) { scanf("%d%lld",&n,&s); int i; for(i=1;i<=n;i++)scanf("%d",&a[i]); long long ans=bisearch(); printf("%d\n",ans); } return 0; }
C题:
众所周知,averyboy是一个非常男孩。天外天给你他一个问题。问题如下:给你一个长度为N的序列a[1]~a[N]和一个整数sum,我们称一个区间为averyboynb区间,当且仅当这个区间的所有数的和不超过sum。现在你需要找出一个区间和最大的averyboynb区间。
枚举左端点,二分查找右端点;
对于每一个左端点,二分求解满足条件的最大的右端点