二分答案
今天讲的是二分答案
二分答案实际上是二分猜答案(好多题差不多,水题可以复制代码AC )
首先我们来看一下目录吧
目录
- 二分答案讲解
- 最大值最小问题
2.1 Luogu P1824 进击的奶牛
2.2 Luogu P1182 数列分段 Section II - 最小值最大问题
3.1 Luogu P1873 砍树 - 其他
4.1 P2440 木材加工
1 二分答案的讲解
- 二分答案有三个步骤(难点)
- 判断这个题是否用到二分这种思想
- 写判断函数
- 写二分部分
3.1 板子1——最小值最大
3.2 板子2——最大值最小
- 再让我们复习一下二分部分的写法
- 分析问题,确定左右半段哪个是符合题意的区间,以及mid归属于哪一段
- 分析结果,选择两种板子
mid = (l + r) / 2;
r = mid, l = mid + 1; //板子1
mid = ( l + r + 1) / 2;
l = mid; r = mid - 1; //板子2
- 判断mid的算法要不要加1!! 判断错误就会出现死循环;
(建议此步骤在草图画两个小圈圈代表最后两个数,把表达式带进去验证,就可以知道是要不要加1了) - 二分终止条件是
l == r;
也是答案所在的位置
2 最大值最小问题(板子2)
2.1 P1824 进击的奶牛
所以我们究竟如何去二分答案呢?
请大家按照1.1 二分答案的步骤执行
- 二分的条件是需要区间具有单调性的,这个我们需要第一位去考虑
所以需要用到sort大法 - 这里的判断函数其实挺好写的,就看间隔是不是大于给定值就好了;但要注意我这种写法初始值为1;因为最少也会有1个
- 第三步就看区间,看mid划分就好了
AC代码如下
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100005];
int n,c;
int jud(int x){
int sum=1, pre = a[1];
for(int i = 2; i <= n; i++){
if((a[i]-pre)>=x){
sum+=1;pre =a[i];
}
}
return sum;
}
int main(){
scanf("%d %d",&n,&c);
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
int l = 0,r = a[n];
while(l < r){
int mid = (l+r+1)/2;
if(jud(mid)>=c) l=mid;
else r=mid-1;
}
printf("%d",r);
return 0;
}
2.2 Luogu P1182 数列分段 Section II
我是不会告诉你这个题和openjudge 一样一样的
OpenJudge 二分查找06:月度开销
-
初始值是为1的,想想为什么??
-
因为这种写法是类似于进位的,就是满了给定值,段数就++;所以初始值是1;
#include<iostream>
#include<cstdio>
#include<algorithm>//最大值最小
using namespace std;
int a[100005];
int n,c,mx,tot;
int jud(int x){
int cnt = 1,sum=0;//cnt初始值为1,思考为什么
for(int i = 1; i<= n; i++){
if(sum+a[i]>x){
cnt++,sum=a[i];
}
else sum+=a[i];
}
return cnt;
}
int main(){
scanf("%d %d",&n,&c);
for(int i = 1; i <= n; i++){
scanf("%d",&a[i]);
mx=max(a[i],mx);
tot+=a[i];
}
int l = mx,r = tot;
while(l < r){
int mid = (l+r)/2;
if(jud(mid)>c) l=mid+1;
else if(jud(mid)<=c) r=mid;
}
printf("%d",l);
return 0;
}
3.1*Luogu P1873 砍树*
一个long long 引发的惨案
因为数据范围过大,所以要用long long !!!
本蒟蒻多次70分后一气之下全部换成了long long
- 还有一点就是要学会用
#define
来代替一些重复且无意义的代码,可以提高代码质量
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll a[1000005];
ll n,c,mx;
ll jud(ll x){ //砍了多少木头
ll sum=0;
for(ll i = 1; i<= n; i++){
if(a[i]>x) sum+=a[i]-x;
}
return sum;
}
int main(){
scanf("%lld %lld",&n,&c);
for(int i = 1; i <= n; i++){
scanf("%lld",&a[i]);
mx=max(mx,a[i]);
}
// printf("%d\n",jud(mx));
ll l = 0,r = mx;
while(l < r){ //最小值最大模板
ll mid = (l+r+1)/2;
if(jud(mid)>=c) l=mid;
else if(jud(mid)<c) r=mid-1;
}
printf("%lld",l);
return 0;
}
Unknown.1 P2440 木材加工
这个题需要注意一下二分部分的写法
各位大佬有更标准的解法告诉偶一下哦
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int a[100005],n,c;
int jud(ll x){
int cnt = 0;
for(int i = 1; i <= n; i++)
cnt += a[i] / x;
return cnt;
}
int main(){
scanf("%d %d",&n,&c);
for(int i = 1; i <= n; i++){
scanf("%d",&a[i]);
}
int l = 0 , r = 210000000;
while(l < r-1){
int mid = (l+r)/2;
if(jud(mid)>=c) l=mid;
else if(jud(mid)< c) r= mid;
}
printf("%d",l);
return 0;
}