1. lower_bound和upper_bound
P2249 【深基13.例1】查找
P1102 A-B 数对
以上两题是可以直接使用STL进行处理的。
仅说一下要关注的点:
- 原数组要进行排序
- 查找区间为[begin,end)
- 注意要减去a
- 如果没有找到想要的数组,那么会返回end
- upper_bound(…)-lower_bound(…)可以求出一段区间长度
2.二分
P1873 EKO / 砍树
在做二分题是总是会遇到一些问题,比如不知道参数怎么调比如
if(m<=tt)return true;
else return false;
为什么是这样写而不是
if(m<tt)return true;
else return false;
由题意明显可知,所要求的是右边界,那么我们画出如下图形,假设那一条蓝线就是我要求的值,那么如果mid超过那个值呢,在这个题目中这就意味着我砍伐的高度变大了,而且超过了这个界限,那么肯定得不到M米的木材,相反如果小于这个边界,那么得到的木材肯定>=M。
所以我们反推一下,当
tt>=m --->在界限内
tt<m ---->在界限外
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int n,m,a[N],l,r;
long long tt;
bool check(int x)
{
for (int i = 1; i <= n; i ++ )
{
if(a[i]>x)tt+=a[i]-x;
}
if(m<=tt)return true;
else return false;
}
int main()
{
cin>>n>>m;
for (int i = 1; i <= n; i ++ )
{
cin>>a[i];
r=r>a[i]?r:a[i];
l=l<a[i]?l:a[i];
}
while(l<r)
{
tt=0;
int mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
}
cout<<r;
return 0;
}
最后来总结一下遇到这类二分题的步骤
- 判断要求的是最大值还是最小值
- 画一个图,假设已经知道界限在哪里了,那么超过这个界限外会是什么情况,把判断的条件写下来
- 通过判断条件反推是否在界限之内
P2440 木材加工
#include <iostream>
#include <cstring>
#include <algorithm>
const int N = 1e5+10;
using namespace std;
int L,n,k,a[N],l,r;
bool test(int x)
{
int s=0;
for (int i = 1; i <= n; i ++ )
{
s+=a[i]/x;
}
//cout<<x<<" "<<s<<endl;
return s>=k;
}
int main()
{
cin>>n>>k;
for (int i = 1; i <= n; i ++ )
{
cin>>a[i];
r=max(r,a[i]);
}
while(l<r)
{
int mid=l+r+1>>1;
if(test(mid))l=mid;
else r=mid-1;
}
cout<<l;
return 0;
}
P2678 NOIP2015 提高组 跳石头
#include<cstdio>
#include<algorithm>
const int N = 5e5+10;
using namespace std;
int a[N];
int d,n,m;
bool judge(int x)
{
int tot=0,i=0,now=0;//tot表示需要搬走的石块数量,i表示找的石头,now表示我站在哪一块石头上
while (i<n+1)
{
i++;
if (a[i]-a[now]<x)
tot++;
else
now=i;
}
if (tot>m) return false;
else return true;
}
int main()
{
scanf("%d%d%d",&d,&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
a[n+1]=d;
int l=1,r=d,mid;
while (l<r)
{
mid=(l+r+1)/2;
if (judge(mid))
l=mid;
else r=mid-1;
}
printf("%d",r);
}
P3853 TJOI2007 路标设置
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int a[100005],l,n,k;
bool jud(int ans){
int cnt = 0;
for(int i=2;i<=n;i++){
if(a[i]-a[i-1] >=ans){
cnt += (a[i] - a[i-1])/ans;
if((a[i] - a[i-1])%ans ==0)
cnt--;
}
}
if(cnt > k) return false;
return true;
}
int main(){
cin>>l>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);
int le = 1,ri = l,mid;
while(le < ri){
mid = (le + ri) / 2;
if(jud(mid)) ri = mid;
else le = mid + 1;
}
cout<<le;
return 0;
}
P1182 数列分段 Section II
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n,m,a[N],l,r;
bool test(int x)
{
int t=1,sum=0;
for (int i = 1; i <= n; i ++ )
{
if(sum+a[i]<=x)
{
sum+=a[i];
}
else
{
t++;
sum=a[i];
}
}
return t<=m;
}
int main()
{
cin>>n>>m;
for (int i = 1; i <= n; i ++ )
{
cin>>a[i];
l=max(l,a[i]);
r+=a[i];
}
while(l<r)
{
int mid=(l+r)>>1;
if(test(mid))r=mid;
else l=mid+1;
}
cout<<l;
return 0;
}