二分模板+习题

整数二分模板

while (left <= right)
{
    int mid = (left + right) / 2;
    if(check(mid)){
    	ans=mid;
    	left = mid + 1;
    }
    else right = mid - 1;
}

double二分模板

for(int i=0; i<100; i++)
{
    double mid = (left + right) / 2;
    if(check (mid)){
        ans = mid;
        left = mid;
    }
    else
        right = mid;
}
while(fabs(right - left) > eps){
    double mid = (left + right) / 2;
    if(check (mid)){
        ans = mid;
        left = mid;
    }
    else
        right = mid;
}

HDU2141

题解:

是否存在满足ai + bj + ck = x。ai + bj打包,二分找是否存在ck = x - ai - bj。

注意一些细节。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 500 + 10;
ll a[N],b[N],c[N],x;
int l,n,m,p;
vector<ll>v;   //注意用ll类型
int main(){
	int caser = 0;
	while(~scanf("%d%d%d",&l,&n,&m)){
		printf("Case %d:\n",++caser);
		v.clear();
		for(int i=1;i<=l;i++)	scanf("%lld",&a[i]);
		for(int i=1;i<=n;i++)	scanf("%lld",&b[i]);
		for(int i=1;i<=m;i++)	scanf("%lld",&c[i]);

		for(int i=1;i<=l;i++)
			for(int j=1;j<=n;j++)
				v.push_back((ll)(a[i]+b[j]));
		v.erase(unique(v.begin(),v.end()),v.end());  //去重
		sort(v.begin(),v.end());    //每次都lower_bound()之前都忘记排序。。。

		scanf("%d",&p);
		while(p--){
			scanf("%lld",&x);
			bool flag = false;
			for(int i=1;i<=m && !flag;i++){   //枚举c,对v进行二分
				ll tmp = x - c[i];
				int k = lower_bound(v.begin(),v.end(),tmp) - v.begin();   //find超时,因为不是二分查询
				if(v[k] == tmp)	flag = true;
			}
			if(flag)	printf("YES\n");
			else	printf("NO\n");
		}
	}
	return 0;
}

POJ3273

题解:

共n天,分成m组,要求花费最大的组值最小。类似于最大值最小化问题。我们可以二分枚举上限mid。max<=mid<=sum。

代码:

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
int const N = 100000 + 10;
int a[N];
int n,m;
bool Judge(int limit){
	int sum = 0,len = 0;
	for(int i=1;i<=n;i++){
		sum += a[i];
		if(sum > limit){
			sum = a[i];
			len++;
		}
	}
	if(++len > m)	return false;   //可能分的少于m组,但是每一组再分还是满足条件
	else return true;
}
int main(){
	while(~scanf("%d%d",&n,&m)){
		int l = 0,r = 0;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			r += a[i];
		}
		l = *max_element(a+1,a+1+n);
		int mid;
		while(l <= r){  
			mid = (l + r) >> 1;
			if(Judge(mid)){    //如果找到了,找更小的
				r = mid - 1;
			}else{
				l = mid + 1;
			}
		}
		printf("%d\n",mid);    
	}
	return 0;
}

POJ3528

题解:

最大化最小值。共有n个石头,给你石头到起点的距离,移走m块石头,使最小距离最大。
二分枚举最小距离mid。如果有小于这个距离的,那么移走石头,直到这个距离大于等于mid。
如果移走的石头大于m就不满足条件。

代码:

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
int const N = 50000	+ 10;
int a[N],n,m,l;
bool Judge(int limit){    //判断限制的最短距离是否满足条件
	int cnt = 0,i = 1,st = 0;
	while(i <= n){
		while(i <= n && a[i] - a[st] < limit)  i++,cnt++;  
		st = i++;	
		if(cnt > m)		return false;
	}
	return true;
}
int main(){
	scanf("%d%d%d",&l,&n,&m);  //n和m可能为0
	for(int i=1;i<=n;i++)	scanf("%d",&a[i]);    
	a[++n] = l;
	sort(a,a+n+1);
	int left = 0,right = l,ans;
	while(left <= right){   //枚举最短距离
		int mid = (left + right) / 2;
		if(Judge(mid)){
			ans = mid;  
			left = mid + 1;
		}else{
			right = mid - 1;
		}
	}
	printf("%d\n",ans);   //left
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值