二分的经典问题 最大化最小值和最小化最大值

有点长~可以选自己想看的部分看~不过建议把刚开始的介绍看完~

不多说,先来一个在升序无重复元素的数组中二分搜索的板子。(l+r)/2=mid可能会爆int这种细节问题我们就放一边。

int a[MAX];

int Binary_Search(int v)
{
	int l=0,r=n-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(a[mid]==v)
			return mid;
		else if(a[mid]>v)
			r=mid-1;
		else
			l=mid+1;
	}
	return l;
}

无重复元素的二分相信大家都会写,就不再赘述了。

下面给一个在非降序有重复元素的数组的二分搜索的板子。

int a[MAX];

int Binary_Search(int v)
{
	int l=0,r=n-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(a[mid]>=v)
			r=mid-1;
		else
			l=mid+1;
	}
	return l;
}

为什么这么写没有问题呢,我们来考虑一下:(1)如果a[mid]>v的时候,令r=mid-1,这时候这样降低上限肯定是没有问题的。(2)如果a[mid]==v的时候,令r=mid-1,这时候会不会有问题呢,我们来仔细分析一下。如果a[mid]==v且这是唯一一个或者说第一个等于v的元素,我们把上限划为mid-1,那么后续的所有操作舍弃掉的必定是左边的区间,二分的最后一步是l==r的情况,这时候a[l]=a[r]=a[mid-1]<v,那么l=mid+1,这时候得到的结果是正确的。如果a[mid]=v然而这不是第一个等于v的元素,我们依旧可以把上限划为mid-1,因为前面还有等于v的元素,所以我们不用担心会出问题。那么l又没有可能取到的并不是第一个等于v的元素呢,仔细思考就会发现其实是不可能的。因为只有a[mid]<v即当前中点的值不满足题意的时候,我们才会令l=mid+1,若此时a [mid+1]==v,那么后续操作必定舍弃右边的区间,并且最后一步a[l]=a[r]=v,此时会令r=mid-1,并不会修改l的值。综上所述,这种方法是没有问题的。只有当mid不合理时,我们才修改下限。这与最小化最大值问题有异曲同工之妙。当judge(mid)不合理时,我们才修改下限

最小化最大值问题:(在满足题意的情况下求最小值)

CSU 1976

http://acm.csu.edu.cn:20080/csuoj/problemset/problem?pid=1976

Description

作为老人的小明非常忧伤,因为他马上要被流放到本部去了,住进全左家垅最有历史感的11舍真是一件非常荣幸的事情。
搬行李是个体力活,小明发现自己的行李太多啦,所以他决定去买很多个袋子来装走。到了超市的小明发现,不同大小的袋子居然价格一样???虽然买最大的自然最赚,但是小明是名远近闻名的环保人士,他觉得袋子只要能装下他的行李就够了,并且为了不麻烦收银的小姐姐(⊙o⊙)…,他也只会购买同一种大小的袋子。因此他希望在能装下所有行李的前提下,袋子越小越好。同时为了避免弄乱行李,小明希望同一个袋子装的是位置连续相邻的行李。
小明摸了摸口袋发现自己带的钱最多能买N个袋子,数学特别差的他不知道到底该买多大的才合适,所以想靠你来解决这个问题了。

Input

第一行为一个数字T(T<=10)表示数据组数
第二行为两个数字N(N <= 10^5)和 M(M <= 10^5)表示袋子个数和小明的行李个数
第三行为M个数字,第i个数字a[i]表示小明的第i个行李体积为a[i](0<a[i] <= 10^9)

Output

输出一行表示袋子的最小体积(整数)

Sample Input

1
3 3
1 1 1

Sample Output

1

Hint

袋子不能装下体积大于其容积的物品
多个物品满足体积之和小于等于一个袋子的容积,就能被装进

题意就不说了吧,直接开始分析:最小容积是在m==n的时候取到的,这时候袋子的最小体积=物品的最大体积,最大容积是在只有一个袋子的时候取到的,这时候袋子的最小体积=物品的体积之和。我们取mid值,看在这种情况下,用掉的袋子的数量是否大于n,因为大于n的时候,这个mid值是必定不满足题意的,因此我们可以放心的令l=mid+1,反之我们令r=mid-1。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<set>
#include<algorithm>
#include<iterator>
#define EPS 1e-12
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

ll a[100005];

int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		ll MAX=0;
		ll sum=0;
		for(int i=0;i<m;i++)
		{
			scanf("%lld",&a[i]);
			sum+=a[i];
			if(a[i]>MAX)
				MAX=a[i];
		}
		ll l=MAX,r=sum;
		while(l<=r)
		{
			ll mid=(l+r)/2;
			int cnt=1;
			sum=0;
			for(int i=0;i<m;i++)
			{
				sum+=a[i];
				if(sum>mid)
				{
					sum=a[i];
					cnt++;
				}
			}
			if(cnt<=n)
				r=mid-1;
			else
				l=mid+1;
		}
		printf("%lld\n",l);
	}
	return 0;
}

 

CSU 1023

http://acm.csu.edu.cn:20080/csuoj/problemset/problem?pid=1023

Description

前段时间,某省发生干旱,B山区的居民缺乏生活用水,现在需要从A城市修一条通往B山区的路。假设有A城市通往B山区的路由m条连续的路段组成, 现在将这m条路段承包给n个工程队(n ≤ m ≤ 300)。为了修路的便利,每个工程队只能分配到连续的若干条路段(当然也可能只分配到一条路段或未分配到路段)。 假设每个工程队修路的效率一样,即每修长度为1的路段所需的时间为1。现在给出路段的数量m,工程队的数量n, 以及m条路段的长度(这m条路段的长度是按照从A城市往B山区的方向依次给出,每条路段的长度均小于1000), 需要你计算出修完整条路所需的最短的时间(即耗时最长的工程队所用的时间)。

Input

第一行是测试样例的个数T ,接下来是T个测试样例,每个测试样例占2行, 第一行是路段的数量m和工程队的数量n,第二行是m条路段的长度。

Output

对于每个测试样例,输出修完整条路所需的最短的时间。

Sample Input

2
4 3
100 200 300 400
9 4
250 100 150 400 550 200 50 700 300

Sample Output

400
900

思路:几乎和上一道题一模一样。在当前mid值的情况下,如果修完所有的路需要的施工队大于要求的,必定不满足题意,这时候我们可以令l=mid+1,反之令r=mid-1。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<set>
#include<algorithm>
#include<iterator>
#define EPS 1e-12
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

int a[305];

int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		int MAX=0;
		int sum=0;
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			sum+=a[i];
			if(a[i]>MAX)
				MAX=a[i];
		}
		int l=MAX,r=sum;
		while(l<=r)
		{
			int mid=(l+r)/2;
			int cnt=1;
			sum=0;
			for(int i=0;i<n;i++)
			{
				sum+=a[i];
				if(sum>mid)
				{
					sum=a[i];
					cnt++;
				}
			}
			if(cnt<=m)
				r=mid-1;
			else
				l=mid+1;
		}
		printf("%d\n",l);
	}
	return 0;
}

 

POJ 3104

http://poj.org/problem?id=3104

 

It is very hard to wash and especially to dry clothes in winter. But Jane is a very smart girl. She is not afraid of this boring process. Jane has decided to use a radiator to make drying faster. But the radiator is small, so it can hold only one thing at a time.

Jane wants to perform drying in the minimal possible time. She asked you to write a program that will calculate the minimal time for a given set of clothes.

There are n clothes Jane has just washed. Each of them took ai water during washing. Every minute the amount of water contained in each thing decreases by one (of course, only if the thing is not completely dry yet). When amount of water contained becomes zero the cloth becomes dry and is ready to be packed.

Every minute Jane can select one thing to dry on the radiator. The radiator is very hot, so the amount of water in this thing decreases by k this minute (but not less than zero — if the thing contains less than k water, the resulting amount of water will be zero).

The task is to minimize the total time of drying by means of using the radiator effectively. The drying process ends when all the clothes are dry.

Input

The first line contains a single integer n (1 ≤ n ≤ 100 000). The second line contains ai separated by spaces (1 ≤ ai ≤ 109). The third line contains k (1 ≤ k ≤ 109).

Output

Output a single integer — the minimal possible number of minutes required to dry all clothes.

Sample Input

sample input #1
3
2 3 9
5

sample input #2
3
2 3 6
5

Sample Output

sample output #1
3

sample output #2
2

题目大意:有一堆衣服和一个散热片,给出每个衣服的潮湿程度,散热片一次只能给烘一件衣服,这衣服每分钟潮湿度减少k,其他的在空气中的衣服每分钟潮湿度减少1,问你使所有衣服全部晾干的最短时间是多少。

思路:最短时间是1,对潮湿程度进行排序,最长时间是a[n-1]。二分判断在当前mid值下,烘干所有衣服所花费的时间是否大于mid,若大于mid,必定不满足题意,我们可以令l=mid+1,否则令r=mid-1。我们用cnt表示烘干衣服所花费的时间,遍历数组,如果a[i]<=mid,那么就没必要用散热片烘干了,反之,我们假设这件衣服需要t次才能烘干,那么必定有t*k+mid-t>=a[i],化简可以得到:t>=(a[i]-mid)/(k-1);用函数ceil(x)可以得到大于等于x的最小整数。(x是double型)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<set>
#include<algorithm>
#include<iterator>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

int a[100005];

int main()
{
	int n,k;
	while(~scanf("%d",&n))
	{
		for(int i=0;i<n;++i)
			scanf("%d",&a[i]);
		scanf("%d",&k);
		sort(a,a+n);
		if(k==1)
		{
			printf("%d\n",a[n-1]);
			continue;
		}
		int l=1;
		int r=a[n-1];
		while(l<=r)
		{
			int mid=(l+r)/2;
			int cnt=0;
			for(int i=0;i<n;++i)
			{
				if(a[i]>mid)//不需要放到散热片上
					cnt+=ceil((a[i]-mid)*1.0/(k-1));
				if(cnt>mid)
					break;
			}
			if(cnt<=mid)
				r=mid-1;
			else
				l=mid+1;
		}
		printf("%d\n",l);
	}
	return 0;
}

 

最大化最小值问题:(在满足题意的情况下求最大值)

POJ 2456

http://poj.org/problem?id=2456

Farmer John has built a new long barn, with N (2 <= N <= 100,000) stalls. The stalls are located along a straight line at positions x1,...,xN (0 <= xi <= 1,000,000,000).

His C (2 <= C <= N) cows don't like this barn layout and become aggressive towards each other once put into a stall. To prevent the cows from hurting each other, FJ want to assign the cows to the stalls, such that the minimum distance between any two of them is as large as possible. What is the largest minimum distance?

Input

* Line 1: Two space-separated integers: N and C

* Lines 2..N+1: Line i+1 contains an integer stall location, xi

Output

* Line 1: One integer: the largest minimum distance

Sample Input

5 3
1
2
8
4
9

Sample Output

3

Hint

OUTPUT DETAILS:

FJ can put his 3 cows in the stalls at positions 1, 4 and 8, resulting in a minimum distance of 3.

Huge input data,scanf is recommended.

题目大意:大概就是把c头羊放到n个椅子上(英语不好ORZ),求羊与羊之间间隔的最大值。

思路:读入n个椅子的位置并进行排序,下限就是a[i]-a[i-1]的最小值,上限就是两头羊的时候,a[n-1]-a[0],然后二分,看在当前mid值的情况下,能放入多少个羊,如果可以放入的羊的个数小于n,此时的mid值必定不满足题意,我们修改r=mid-1(注意!这里是修改r而不是修改l,因为要找最大值,所以我们在不满足题意的情况下才修改上限);反之我们令l=mid+1。当然最后打印的值也要更改为r。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<set>
#include<algorithm>
#include<iterator>
#define INF 0x3f3f3f3f
using namespace std;

int a[100005];

int main()
{
	int n,c;
	scanf("%d %d",&n,&c);
	for(int i=0;i<n;++i)
		scanf("%d",&a[i]);
	sort(a,a+n);
	int MIN=INF;
	for(int i=1;i<n;++i)
		if(a[i]-a[i-1]<MIN)
			MIN=a[i]-a[i-1];
	int l=MIN,r=a[n-1]-a[0];
	while(l<=r)
	{
		int mid=(l+r)/2;
		int cnt=1;
		int t;
		int cur=a[0];
		while(1)
		{
			t=lower_bound(a,a+n,cur+mid)-a;
			if(t!=n)
			{
				++cnt;
				cur=a[t];
			}
			else
				break;
		}
		if(cnt<c)
			r=mid-1;
		else
			l=mid+1;
	}
	printf("%d\n",r);
	return 0;
}

 

目前刷的题还是太少了,不知道这种二分写法有没有局限性,欢迎指出不足! 

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值