【入门】二分答案题解

这套题还是蛮简单的。。。

P1873 [COCI 2011/2012 #5] EKO / 砍树
不难发现锯子越低,砍伐的树木就越长。我们直接二分锯子的高度,然后 O ( N ) O(N) O(N) 代入回去check即可

#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e6+10;
int n,m;
int a[N];
bool check (int x) {
    LL sum = 0;
    for (int i = 1;i <= n;i++) sum += max (a[i]-x,0);
    return sum >= m;
}
int main ()  {
    cin >> n >> m;
    for (int i = 1;i <= n;i++) cin >> a[i];
    int l = 0,r = N;
    while (l < r) {
        int mid =( l + r +1 )/2;   //记得+1
        if (check (mid)) l = mid;   //如果当前点合法,那么答案可以更大,并且mid也有可能成为答案,所以l = mid
        else r= mid-1;   //对应过来的
    }
    cout << l << endl;
    return 0;
}

P2440 木材加工
直接二分答案,对于答案而言,代入回每段木头check,利用整除就能计算出某段木头能切割出多少。本题涉及到无解情况,注意判断。

#include<bits/stdc++.h>
using namespace std;
int A[100005];
int n,k;
bool check(int x){
	long long int sum=0;
	if(x==0)return true;
	for(int i=1;i<=n;i++){
		sum=sum+A[i]/x;
		if(sum>=k)return true;
	}
	return false;
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d",&A[i]);
	}
	int L=0,R=1e8;
	while(L<R){
		int mid=(L+R+1)/2;
		if(check(mid)){
			L=mid;
		}
		else{
			R=mid-1;
		}
	}
	printf("%d",L);
	return 0;
}

P2678 [NOIP2015 提高组] 跳石头
因为起点和终点也是石头,先把两端确定下来,
也就是 D [ 0 ] = 0 , D [ N + 1 ] = L D[0]=0,D[N+1]=L D[0]=0,D[N+1]=L。考虑二分跳跃距离x,直接从左往右枚举石头,计算出相邻石头的跳跃距离并求和,如果该求和距离<x,我们记录石头数量,因为这些石头都是需要被搬走的。如果求和距离大于等于x,那么我们需要重新计算跳跃的距离。这样子能保证我们搬走最少的石头且跳跃的间距尽可能的大。如果最后一段一直跳跃到终点,距离和还是<x,那么需要再搬走一块石头,相当于把上一次的落脚点石头直接移除掉,这样子一步跳跃到终点,满足了“跳跃距离至少为x”

#include<bits/stdc++.h>
using namespace std;
int D[50005];
int n,L,m;
bool check(int x){
	int cnt=0;
	int len=0;//累计跳跃长度 
	int need=0;
	for(int i=1;i<=n+1;i++){
		int dis=D[i]-D[i-1];//石头间距
		len=len+dis;
		cnt++;
		if(i==n+1&&len<x){
			need=need+cnt;//把上一次的落脚点也搬走
		}
		if(len>=x){
			need=need+(cnt-1);//cnt-1是因为我们需要一个落脚点石头
			len=0;
			cnt=0;
		}
	}
	if(need>m)return false;
	return true;
}
int main(){
	scanf("%d%d%d",&L,&n,&m);
	D[0]=0;//起点 
	D[n+1]=L;//终点 
	for(int i=1;i<=n;i++){
		scanf("%d",&D[i]);
	}
	int l=1,r=L;
	while(l<r){
		int mid=(l+r+1)/2;
		if(check(mid)){
			l=mid;
		}
		else{
			r=mid-1;
		}
	}
	printf("%d",l);
	return 0;
}
/*
8 3 1
2
4
7
*/

P1182 数列分段 Section II
二分答案,表示某一段和的最大值x。考虑从左往右计算,依次来凑x。
不要超过x的值,如果超过了应该另起一段,段数++。最终判断生成的段数跟M的关系。如果在当前答案下,段数小于M,说明我们的二分的答案比较大,我们尝试缩小答案; 如果段数等于M,说明我们还有缩小答案的空间;如果段数大于M,说明我们二分的答案比较小,应该放大答案范围

#include<bits/stdc++.h>
using namespace std;
int n;
int A[100005];
int m;
bool check(int x){
	int cnt=1;
	int s=0;
	for(int i=1;i<=n;i++){
		if(s+A[i]<=x){
			s=s+A[i];
		}
		else{
			s=A[i];
			cnt++;
		}
	}
	if(cnt<=m)return true; 
	else return false;
}
int main(){

	cin>>n>>m;
	int L=0,R=1e9;
	for(int i=1;i<=n;i++){
		cin>>A[i];
		L=max(L,A[i]);
	}
	while(L<R){
		int mid=(L+R)/2;
		if(check(mid)){//假设划分段的和最大值是mid  
			R=mid;
		}
		else{
			L=mid+1;
		}
	}
	cout<<R;
    return 0;
}
  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值