二分搜索专题题解

导航栏

页面内跳转跳转到题目页面
APOJ_2456_Aggressive cows
BPOJ_1064_Cable master
CPOJ_3258_River Hopscotch
DPOJ_3104_Drying
EPOJ_3111_K Best
FPOJ_3579_Median
A POJ_2456_Aggressive cows 回到顶部

题目大意:有C头牛要放入N间牛舍中,每间牛舍在xi位置上,要求每头牛之间的间距尽可能的大。求最大化后最近两头牛之间的距离。
解题策略:从0~INF之间寻找最适合的值,寻找答案的过程可用二分。

#include <iostream>
#include <algorithm>
using namespace std;
//输入
int N, C;
int x[100005];

bool judge(int n) {	//判断每头牛最少相距n单位,是否可以放入N见牛舍 
	//先将一头牛放入第一间牛舍 
	int last = 0;  //标记当前最后一头牛放入的牛舍位置(下标) 
	for(int i = 1; i < C; i++) {	//放牛 
		int pos = last;	//试探第i头牛放入位置 
		while(pos < N && x[pos]-x[last] < n) { //保证距离不小于d 
			pos++; 
		}
		if(pos == N) {	//超出牛舍范围,距离太大 
			return false; 
		}
		last = pos;	//第i头牛放入pos位置 
	}
	return true; 
}
int main()
{
	while(cin >> N >> C) {
		for(int i = 0; i < N; i++) {
			cin >> x[i];
		}
		sort(x, x+N);	//对牛舍按照从左到右排序(从小到大)
		
		int l = 0, r = x[N-1];  //距离肯定不会超过第一间与最后一间牛舍之间的距离,所以可以INF = x[N-1]
		while(l <= r) {
			int mid = l + (r-l) / 2;	//防止数据溢出[  l+(r-l)/2 = l+r/2-l/2 = l/2+r/2 = (l+r)/2]; 
			if(judge(mid)) {	//如果相距mid可以将搜索范围中的值扩大(即搜索序列右侧) 
				l = mid + 1;
			}else {
				r = mid - 1; //答案不满足,将搜索范围中的距离缩小(即搜索左侧)
			} 
		} 
		int res = l - 1;	//保存答案 
		cout << res << endl;
	}
	return 0;
}
B POJ_1064_Cable master 回到顶部

题目大意:有N条绳子需要平均分为K条,每条绳子的长度为xi米(小数点后两位),平均分后的K条绳子在1~100米之间。问分后绳子最长为多少(保存小数点后两位)。
解题策略:二分搜索答案,先将题目中的数据统一为厘米单位(化为整数),然后求解答案时除以100.0并保存两位小数。

#include <iostream>
#include <cstdio>
using namespace std;
//输入
int N, K;
int value[10005];	//将输入绳子长度*100化为整数(厘米),数据都统一为厘米求解 

bool judge(int n) {	//判断分为n厘米的绳子是否合适 
	int num = 0;	//记录可分为多少条长度为n的绳子 
	for(int i = 0; i < N; i++) {
		num += value[i] / n;
	} 
	if(num >= K) {	//可以分到K条,即符合条件 
		return true;
	}
	return false;
}
int main()
{
	while(cin >> N >> K) {
		double length;
		for(int i = 0; i < N; i++) {
			cin >> length;
			value[i] = length*100;
		}
		
		int l = 1, r = 100*1000*100;	//题目描述输入的每条绳子在1米~100公里,且平分后绳子最短不小于1厘米
		while(l <= r) {
			int mid = (l + r) / 2;	
			if(judge(mid)) {	//符合条件,试探更大的答案 
				l = mid + 1;
			}else {		//不符合条件,减小答案区间值 
				r = mid - 1;	
			}
		} 
		double res = (l - 1) / 100.0;
		printf("%.2lf\n", res);
	} 
	return 0;
}
C POJ_3258_River Hopscotch 回到顶部

题目大意:有条宽为L的河流中间有N块石头,每块石头距离河的一边(假设为起点)为di单位,农民可以拿掉M块石头使每块石头之间的间距变大(河的两边也看做为两块石头)。求拿走石头后两石头间距最小的最大值

#include <iostream>
#include <algorithm>
using namespace std;
//输入
int L, N, M;	
int d[50005];

bool judge(int n) {	  
	int num = 0;	//记录可以拿掉石头个数 
	int last = 0; 	//当前所在石头位置 
	for(int i = 1; i <= N+1; i++) {	
		if((d[i] - last) <= n) {	//当相邻石头距离不大于n时,可以直接拿掉该石头 
			num++; 	
		}else {
			last = d[i];	//跳到该石头位置 
		}
		if(num > M) {	//如果可拿掉石头超过M,不符合要求,减小距离 
			return true;
		}
	} 
	return false;
}
int main()
{
	while(cin >> L >> N >> M) {
		for(int i = 1; i <= N; i++) {
			cin >> d[i];
		}
		d[0] = 0, d[N+1] = L; //加入河的两岸作为起点与终点
		sort(d, d+N+2); 
		
		int l = 0, r = L;	//最大值为河的宽度 
		while(l <= r) {
			int mid = l + (r-l) / 2;
			if(judge(mid)) {
				r = mid - 1; 
			}else {
				l = mid + 1;
			}
		} 
		int res = r + 1;
		cout << res << endl;
	}	
	return 0;
}

D POJ_3104_Drying 回到顶部

题目大意:有N件刚洗后的衣服,每件衣服的含水量是ai,有台烘干机(每次仅可烘干一件衣服且仅有一台烘干机)每分钟可烘干 k 单位水(如使用烘干机,最少不低于1分钟),衣服自然烘干为每分钟1单位水。问至少需要多少分钟可使所有衣服全部烘干。
题目策略:二分自不多说,需要考虑的是判断试探值是否合适(mid)。
1. ai <= mid 表示衣服可以在试探值内自然烘干
2. ai > mid 需要使用烘干机,假设使用 ti 分钟的烘干机, 则对于该衣服应满足 ai-t*k+t <= mid,且所有ti总和不超过mid
推荐使用scanf()/printf()。c++输入输出流会超时

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm> 
using namespace std;
//输入
int N, K;
int a[100005];

bool judge(int n) {	//判断 
	long long sum = 0;
	for(int i = 0; i < N; i++) {
		if(a[i] > n) { //情况2,求烘干时间time 
			//ai-t*k+t <= mid相当于t >= (mid-ai) / (1-k) (除以负数要变好),
			//即t = ceil((ai-mid)/(k-1));不足一分钟按一分钟计算,所以向上取整(ceil) 
			if(K == 1) {
				return false;	//显然不满足 
			}
			int t = ceil((a[i]-n)*1.0/(K-1));
			sum = sum + (long long)t;
		}
	}
	if(sum > n) {
		return false;
	}
	return true;
}
int main()
{
	while(scanf("%d", &N) != EOF) {
		int max_time = 0;
		for(int i = 0; i < N; i++) {
			scanf("%d", &a[i]);
			max_time = max(max_time, a[i]);
		} 
		scanf("%d", &K);
		
		int l = 0, r = max_time;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(judge(mid)) {
				r = mid - 1;
			}else {
				l = mid + 1;
			}
		}
		int res = r + 1;
		printf("%d\n", res);
	}
	return 0;
}

E POJ_3111_K Best 回到顶部

题目大意: 有N个价值为vi,重量为wi的物品,从中选择K个物品使得单位重量的价值最大。 输出一种最大的选择情况(输出选择物品的编号)
解题策略:判断mid是否符合,即应sum(v[0K])/sum(w[0K]) >= mid;(K件物品需要贪心选择),即
–>sum(v[0K])/sum(w[0K]) - mid >= 0
–>sum(v[0~K]) - midsum(w[0~K]) >= 0;
–>sum(v[0~K]) - sum(wi
mid)>= 0; (i = 0~K)
–>sum(vi - wimid) >= 0; (i = 0~K);
即在判断之前将N物品按照vi-wi
mid从大到小排序,选择前K间并判断和是否大于等于0,如果大于等于0则表示mid满足,可以试探更大的值

#include <iostream>
#include <algorithm> 
#include <cstdio>
using namespace std;
const int INF = 1e7; //二分右边界(由题意可看出单位重量最大价值不超过1e6) 
//输入
int N, K;
typedef struct {
	int v, w; //体积与价值 
	double cost; //cost = v - w * mid; 
	int id;	//该物品的编号(下标+1) 
}Data;
Data value[100005];

//此处用引用节省时间,否则会超时 
bool cmp(Data &d1, Data &d2) {	//传入引用参数, 引用相当于操作的实参的地址 
	return d1.cost > d2.cost;	
}

bool judge(double n) {	
	for(int i = 0; i < N; i++) {
		value[i].cost = value[i].v - value[i].w * n;
	}
	sort(value, value+N, cmp);	//排序,选择前K件(详情看解题策略)	 
	
	double sum = 0;	 
	for(int i = 0; i < K; i++) {	
		sum += value[i].cost; 
	}
	if(sum >= 0) {	 
		return true;
	}
	return false;
}
int main()
{
	while(scanf("%d%d",&N, &K) != EOF) {
		for(int i = 0; i < N; i++) {
			scanf("%d%d", &value[i].v, &value[i].w);
			value[i].id = i+1;
		}
		 
		double l = 0, r = (double)INF;
		for(int i = 0; i < 100; i++) {	//由于浮点数精度问题,所以可以直接选择循环100次(精度可达到(10^-30)范围,一般可解决问题) 
			double mid = (l + r) / 2;
			if(judge(mid)) {
				l = mid;
			}else {
				r = mid;	
			}
		} 
		
		for(int i = 0; i < K - 1; i++) {	//输出方案 
			printf("%d ", value[i].id);
		}
		printf("%d\n", value[K-1].id); 
	}
	return 0;
}
F POJ_3579_Median 回到顶部

题目大意:给定有N个数字组成的序列,构造一个由本序列两两元素差值的绝对值构成的新序列,求新序列的中位数。
解题策略:二分(.)。判断探测值是否是满足条件的中位数时,当小于该中位数的数字个数大于等于该中位数位置时,则符合条件,试探更小的值

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
//输入
int N;
int x[100005];

int cnt;
bool judge(int n) { 
	int num = 0;	//记录中位数前的数字个数 
	for(int i = 0; i < N-1; i++) {	//依次遍历|xi-yi|所有情况
		//uperr_bound()(二分查找函数)返回第一个大于x[i]+n的元素位置
		int t = upper_bound(x+i+1, x+N, x[i]+n) - (x+i+1);	 //求以x[i]为其中一个元素做差值构成的序列小于中位数的数字个数 
		num += t;
		if(num >= cnt) {
			return true;
		}
	}
	return false;
}	 
int main()
{
	while(scanf("%d", &N) != EOF) {
		for(int i = 0; i < N; i++) {
			scanf("%d", &x[i]);
		}
		sort(x, x+N);
		
		cnt = N*(N-1)/2;	//求|xi-xj|序列数字个数 
		cnt = (cnt+1)/2;	//求中位数位置 
		int l = 0, r = x[N-1];
		while(l <= r) {
			int mid = (l + r) / 2;
			if(judge(mid)) {
				r = mid - 1;
			}else {
				l = mid + 1;
			}
		} 
		int res = r + 1;
		cout << res << endl;
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值