7.24 二分搜索
二分搜索作用:降低时间复杂度到log(n);求满足条件的最大的最小值,或是最小最大值;
设计一个bool judge 函数,判断该点是否合法(满足条件)
A题:
还记得我们新生赛上的这题Averyboy的筷子这题吗?众所周知,Averyboy是一个非常的男孩,既然是一个非常的男孩,那么他就会有许多奇葩的爱好。比如收藏筷子。现在美美旸有n双筷子,编号为1-n,同一双筷子编号相同,美美旸把这些筷子放成一排。现在美美旸对av-boy说,如果你能解决我给你出的一个问题,这些筷子都归你。美美旸的问题是:把这些筷子通过一些操作,使得最后这些筷子是同一双的一定相连。每次的操作是,你可以从这2n支筷子中任意取出一支,剩下的筷子自动合并在一起,然后把这支筷子插入任意一个位置,包括两端。(操作次数无限制。)为了***难av-boy,美美旸故意定了一个条件,每次只能取出编号大于等于x的筷子。现在你的问题是找出最大的x(1 <= x <= n),使得av-boy一定能顺利拿到筷子。
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元,你最多能买多少件物品.
#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区间。
枚举左端点,二分查找右端点;
对于每一个左端点,二分求解满足条件的最大的右端点
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int maxn=1e5+10; 5 int n; 6 ll sum; 7 int a[maxn]; 8 ll pre[maxn]; 9 bool judge(int n,int left) 10 { 11 if(pre[n]-pre[left-1]<=sum)return true; 12 else return false; 13 } 14 int main() 15 { 16 int t; 17 cin>>t; 18 while(t--) 19 { 20 scanf("%d%lld",&n,&sum); 21 int i; 22 pre[0]=0; 23 for(i=1;i<=n;i++) 24 { 25 scanf("%d",&a[i]); 26 pre[i]=pre[i-1]+a[i]; //前缀和数组 很大程度上减小时间复杂度 27 } 28 ll res=0; 29 for(i=1;i<=n;i++) //枚举左端点 30 { 31 int l=i; 32 int r=n; 33 int ans=0; 34 while(l<=r) //二分查找右端点 35 { 36 int mid=(l+r)>>1; 37 if(judge(mid,i)) 38 { 39 ans=mid; 40 l=mid+1; //找满足条件的最大的右端点 41 } 42 else r=mid-1; 43 } 44 if(ans>0)res=max(res,pre[ans]-pre[i-1]); 45 } 46 if(res>0)printf("%lld\n",res); 47 else printf("averyboynb\n"); 48 } 49 return 0; 50 }
D题:
美旸旸现在在腾讯实习。他每工作一天,公司就会给他发一天的工资,但是,当天的工资不一定要当天领,可以等到下次领的时候一起领,但是每次领工资时,之前所有没有领的工资必须这次都领完。现在给你美旸旸工作N天的工资a[1]~a[N],然后给你一个整数M,代表美旸旸一共要领M次工资(不能多也不能少)并且最后一定要把N天的工资领完,毕竟都是美旸旸赚的血汗钱。也就是第N天他是一定要领工资的。(不然就领不完工资了。。。)。现在问题是让你帮美旸旸选择一种领工资的方案,使得这M次领的工资的最大值最小,毕竟美旸旸是一个低调的人,他不想一次领太多钱。你能帮助他吗?
Sample Input
1
7 5
100
400
300
100
500
101
400
Sample Output
500
此题为典型的二分搜索题,即找最小最大值
设计bool judge函数,判断当最大值为k时,需要领工资的次数是否<=M,是则合法,否则非法。
尽可能最多地领工资,一直领到存储的值会大于最大值k
二分查找最小的最大值k
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int maxn=1e5+10; 5 int n,tmax; 6 int a[maxn]; 7 bool judge(ll x) 8 { 9 int i; 10 ll sum=0; 11 int t=0; 12 for(i=1;i<=n;i++) 13 { 14 if(sum+a[i]>x) 15 { 16 t++; //在领到a[i]这个工资之前,必须要领一次工资了 17 sum=a[i]; //领完一次工资之后现在账户存储的值 等价于sum=0;sum+=a[i]; 18 } 19 else sum+=a[i]; //尽可能的存储最大的工资 20 } 21 if(sum)t++; 22 if(t>tmax)return false; 23 else return true; 24 } 25 int main() 26 { 27 int t; 28 cin>>t; 29 while(t--) 30 { 31 int i; 32 scanf("%d%d",&n,&tmax); 33 for(i=1;i<=n;i++)scanf("%d",&a[i]); 34 int r=0,l=-1; 35 ll ans=0; 36 for(i=1;i<=n;i++) 37 { 38 r+=a[i]; // 上界是所有的工资之和 39 l=max(l,a[i]); //下界是单日工资中最大的那一个 40 } 41 while(l<=r) 42 { 43 int mid=(l+r)>>1; //算出来的mid不一定是某些工资的和,但是最后一定会减小到那些存在的和。 44 if(judge(mid)) 45 { 46 ans=mid; 47 r=mid-1; //二分查找满足条件的最小的最大值 48 } 49 else l=mid+1; 50 } 51 printf("%lld\n",ans); 52 53 } 54 return 0; 55 }