Phil的课堂笔记之——二分答案

二分答案

今天讲的是二分答案
二分答案实际上是二分猜答案(好多题差不多,水题可以复制代码AC
首先我们来看一下目录吧

目录

  1. 二分答案讲解
  2. 最大值最小问题
    2.1 Luogu P1824 进击的奶牛
    2.2 Luogu P1182 数列分段 Section II
  3. 最小值最大问题
    3.1 Luogu P1873 砍树
  4. 其他
    4.1 P2440 木材加工

1 二分答案的讲解

  • 二分答案有三个步骤(难点)
  1. 判断这个题是否用到二分这种思想
  2. 写判断函数
  3. 写二分部分
    3.1 板子1——最小值最大
    3.2 板子2——最大值最小
  • 再让我们复习一下二分部分的写法
  1. 分析问题,确定左右半段哪个是符合题意的区间,以及mid归属于哪一段
  2. 分析结果,选择两种板子
	mid = (l + r) / 2;
	r = mid, l = mid + 1; 		//板子1
	mid = ( l + r + 1) / 2;
	l = mid; r = mid - 1;		//板子2
  1. 判断mid的算法要不要加1!! 判断错误就会出现死循环;
    建议此步骤在草图画两个小圈圈代表最后两个数,把表达式带进去验证,就可以知道是要不要加1了
  2. 二分终止条件是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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值