c++专题二
c++专题二学习日记
二分法
1.基本模版
(1)l<=r
int l,r,ans;
while(l<=r){
int mid=l+r >> 1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
(2)l<r
int l,r;
while(l<r){
int mid=l+r+1 >>1;
if(check(mid)) l=mid;
else r=mid-1;
}
return l;
or
while(l<r){
int mid=l+r-1 >>1;
if(check(mid))r=mid;
else l=mid+1;
}
return r;
2.做题
(1)A-B数对
应该用二分查找符合A=B+C的A,不过我用了map就不改了
int main(){
int n,c;
cin>>n>>c;
unordered_map<int,int> numbers;
long long count=0;
vector<int> a(n);
int i;
for(i=0;i<n;i++){
cin>>a[i];
numbers[a[i]+c]+=1;
}
sort(a.begin(),a.end()); //用algorithm中的sort排序数组a
for(i=0;i<n;i++){
count+=numbers[a[i]];
}
cout<<count<<endl;
return 0;
}
(2)抄书
思路:
[1]用贪心算法来check找到的mid可不可行(第1个人抄,抄到超过mid后换下一个人抄,以此类推;
[2]用二分找到mid;
[3]因为要让前面的人少抄,贪心要从后往前走
#include<iostream>
using namespace std;
const int K=500;
int m,k,mid;
int a[K+10],s[K+10],ed[K+10];
bool check(int mid){ //[1]
int cnt=1,now=mid;
int i;
for(i=1;i<=m;i++){
if(a[i]>now){
cnt++;
i--;
now=mid;
}
else
now-=a[i];
}
return cnt<=k;
}
int main(){
cin>>m>>k;
int i,max=0,sum=0;
for(i=1;i<=m;i++){
cin>>a[i];
if(a[i]>max)max=a[i];
sum+=a[i];
}
int l=max,r=sum;
while(l<r){ //[2]
mid=(l+r-1) >> 1;
if(check(mid)){
r=mid;
}
else
l=mid+1;
}
ed[k]=m;
s[1]=1;
int j,now=r; //[3]
for(i=k,j=m;i>0;i--){
for(;now>=a[j]&&j>0;j--){
now-=a[j];
}
s[i]=j+1;
ed[i-1]=j;
now=r;
}
for(i=1;i<=k;i++){
cout<<s[i]<<' '<<ed[i]<<endl;
}
return 0;
}
(3)青蛙过河
思路:
假设青蛙跳跃长度为y,那么对任意一个区间[l,l+y-1],高度和一定大于2x。如果每段高度和都大于2x,那么对于0~n的任意一点a,都有大于等于2x种途径跳上a,所以只要保证任意一段长度为y的区间,高度和大于2x,青蛙跳跃能力为y时就可以上岸。
#include<iostream>
using namespace std;
const int N=100005;
int n,x;
int h[N];
long long s[N];
int l,r,mid;
bool check(int mid){
int i;
for(i=0;i+mid-1<n;i++){
int j=i+mid-1;
if(s[j]-s[i-1]<2*x)
return false;
}
return true;
}
int main(){ //前缀和:
cin>>n>>x; sum[1]=h[1]
int i; sum[2]=h[1]+h[2]
for(i=1;i<n;i++){ sum[3]=h[1]+h[2]+h[3]...
cin>>h[i];
s[i]=s[i-1]+h[i]; [L,R]=sum[R]-sum[L-1]
}
l=1;
r=n;
while(l<r){
mid=l+r >> 1;
if(check(mid))
r=mid;
else
l=mid+1;
}
cout<<l<<endl;
return 0;
}