蓝桥杯专题之二分篇

题目列表:

2017年:分巧克力

2018年:递增三元组

跳石头

攻击性的牛

2021年:杨辉三角形

1.分巧克力

题目描述

儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。

为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:

    1. 形状是正方形,边长是整数  
    2. 大小相同  

例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。

当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?

输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)  
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000) 
输入保证每位小朋友至少能获得一块1x1的巧克力。   

输出
输出切出的正方形巧克力最大可能的边长。

样例输入:

2 10  
6 5  
5 6 
 样例输出:

2

分析: 

从1~100000中寻找一个可以满足条件的尽可能大的数,100000比较大所以选用二分法来做

 

犯的错点:

在所给的巧克力中选出它们长和宽中的最小值作为右边界,比如给你三块巧克力,一块56*72,一块100*34,一块30*2,它们中的最小值是2,所以我在1~2中二分查找,但是错就错在题目并没有说你要切分完所有的巧克力,只要我按n*n来切分够K份就行了,所以右边界应该是100000

代码:

#include<iostream>
#include<algorithm>
using namespace std; 
int N,K,ans;
const int Max_N = 1e5;
int H[Max_N],W[Max_N];
int check(int n){
	int sum = 0;
	for(int i = 0;i < N;i++){
		sum += (H[i]/n)*(W[i]/n);
	}
	if(sum >= K){
		return 1;
	}
	return 0;
}
int main(){
	cin >> N >> K;
	for(int i = 0;i < N;i++){
		cin >> H[i] >> W[i];
	}
	int left = 1;
	int right = 1e5; 
	while(left <= right){
		int mid = left + (right - left) / 2;
		if(check(mid)){ //找到一个符合条件的记录一下,继续向右寻找可能的最大值 
			ans = mid;
			left = mid + 1;
		}else{
			right = mid - 1;
		}
	}
	cout << ans << endl;
	return 0;
}

2.递增三元组

题目描述

给定三个整数数组
A = [A1, A2, ... AN],
B = [B1, B2, ... BN],
C = [C1, C2, ... CN],
请你统计有多少个三元组(i, j, k) 满足:
1. 1 <= i, j, k <= N  
2. Ai < Bj < Ck  

【输入格式】
第一行包含一个整数N。
第二行包含N个整数A1, A2, ... AN。
第三行包含N个整数B1, B2, ... BN。
第四行包含N个整数C1, C2, ... CN。

对于30%的数据,1 <= N <= 100  
对于60%的数据,1 <= N <= 1000
对于100%的数据,1 <= N <= 100000 0 <= Ai, Bi, Ci <= 100000

【输出格式】
一个整数表示答案

【样例输入】
3
1 1 1
2 2 2
3 3 3

【样例输出】
27

分析:

对A,B,C组的元素进行排序,然后以B组元素作为中间媒介,在A组中找比B[i]小的元素个数(假定有j个) ,在C组中找比B[i]大的元素个数(假定有k个),那么当中间元素是B[i]时,满足A<B<C的就有 j * k个。

唯一要注意的一点是j*k可能会超出int的范围,所以需要强制转换为long long 型

代码: 

#include<iostream>
#include<algorithm>
using namespace std;
const int MAX_N = 1e5+10;
int a[MAX_N],b[MAX_N],c[MAX_N];
int main(){
	int N;
	cin >> N;
	for(int i = 0;i < N;i++){
		cin >> a[i];
	}
	for(int i = 0;i < N;i++){
		cin >> b[i];
	}
	for(int i = 0;i < N;i++){
		cin >> c[i];
	}
	sort(a,a+N);
	sort(b,b+N);
	sort(c,c+N);

	long long ans = 0;
	for(int i = 0;i < N;i++){
		int j = lower_bound(a,a+N,b[i]) - a;//lower_bound():找第一个大于等于b[i]的指针
		int k = c+N - upper_bound(c,c+N,b[i]);//upper_bound():找第一个大于b[i]的指针
		ans += (long long)j*k;
	}
	cout << ans << endl;
	return 0;
}

3.跳石头

题目描述
一年一度的「跳石头」比赛又要开始了!

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走M 块岩石(不能移走起点和终点的岩石)。

输入描述
输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。

接下来 N 行,每行一个整数,第 i 行的整数 Di(0 < Di < L)表示第 i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

其中,0≤M≤N≤5×1e4 ,1≤L≤1e9 。

输出描述
输出只包含一个整数,即最短跳跃距离的最大值。

样例输入
25 5 2
2
11
14
17
21
样例输出
4

分析:

本题要我们求的就是满足条件C(x)的最大的最短跳跃距离x

条件C(x) :在满足最短跳跃距离x的情况下需要移走的石头的数量不能超过M

代码:

#include<iostream>
using namespace std;
const int MAX_N = 5*1e4+10;
const long long INF = 1e9+10;
long long L,N,M;
long long d[MAX_N];
bool check(long long x){
	int cnt = 0;
	int last = 0;
	for(int i = 1;i <= N;i++){
		if(d[i]-d[last] < x){//如果移走一块石头不行的话,就继续移,直到last和i之间的距离>=x 
			cnt++;
		}else{
			last = i;
		}
	}
	if(cnt <= M){
		return true;
	}
	return false;
}
int main(){
	cin >> L >> N >> M;
	for(int i = 1;i <= N;i++){
		cin >> d[i];
	}
	long long l = 0,r = L;
	long long ans = 0;
	while(l <= r){
		long long mid = (l+r)/2;
		if(check(mid)){
			ans = mid;
			l = mid + 1;
		}else{
			r = mid - 1;
		}
	}
	cout << ans;
	return 0;
}

4.攻击性的牛

题目描述

John建造了一个有N(2<=N<=100,000)个隔间的牛棚,这些隔间分布在一条直线上,坐标是x1,...,xN (0<=xi<=1,000,000,000)。

他的M(2<=M<=N)头牛不满于隔间的位置分布,因此经常互相攻击。为了防止牛之间的互相打斗,Farmer John想把这些牛安置在指定的隔间,所有牛中相邻两头的最近距离越大越好。那么,这个最大的最近距离是多少呢?

输入:

N = 5

M = 3

x = {1,2,8,4,9}

输出:

3(在位置为1,4,9的牛舍中放入三头牛)

分析:

本题的意思就是让我们求满足条件C(x)的最大的最近距离x

条件C(x):这个最近距离x至少让M头牛不能相互攻击,换句话说,就是原本有N头牛,为了满足这个最近距离x,我需要拿掉几头牛让剩下的牛的数量大于等于M,则这个最近距离是可以的

代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int MAX_N = 5*1e4+10;
long long L,N,M;
long long d[MAX_N];
bool check(long long x){
	int cnt = 0;
	int last = 0;
	for(int i = 1;i < N;i++){
		if(d[i]-d[last] < x){ 
			cnt++;
		}else{
			last = i;
		}
	}
	if(N-cnt >= M){
		return true;
	}
	return false;
}
int main(){
	cin  >> N >> M;
	for(int i = 0;i < N;i++){
		cin >> d[i];
	}
	sort(d,d+N);
	long long l = 0,r = d[N-1];
	long long ans = 0;
	while(l <= r){
		long long mid = (l+r)/2;
		if(check(mid)){
			ans = mid;
			l = mid + 1;
		}else{
			r = mid - 1;
		}
	}
	cout << ans;
	return 0;
}

5.杨辉三角形

分析:

1.首先我们知道出现杨辉三角只对称的,说明出现在右边的数左边一定也有,所以我们直接舍弃右半边,只看左半边

2.借助杨辉三角的一个组合数性质快速定位这个数

N = C_{i}^{j}\textrm{} 

C_{i}^{j}\textrm{} 所在的位置: 1+2+3+ ……+i + j + 1(其中i是行号,j是列号)

例:C_{4}^{2}\textrm{} 所在的位置: 1 + 2 + 3 + 4 + 2  + 1 =  13,说明6是第13个数

所以我们只要知道N这个数的i和j,就能知道他是第几个数

代码(50分): 

#include<iostream>
using namespace std;
long long N;
long long check(int a,int b){//计算组合数
	long long res = 1;
	for(int i = a,j = 1;j <= b;i--,j++){
		res = res*i / j;
		if(res > N){//如果在计算的过程中res已经大于N,就提前返回,没有必要再继续算下去了
			return res;
		}
	}
	return res;
}

int main(){
	cin >> N;
	for(int i = 0;;i++){
		int l = 0,r = (i+1)/2;//r是中间的分界线
		while(l <= r){
			int mid = (l+r)/2;
			int t = check(i,mid);
			if(t == N){
				int sum = 0;
				for(int k = 1;k <= i;k++){
					sum += k;
				}
				cout << sum + mid + 1;
				return 0;
			}else if(t < N){
				l = mid + 1;
			}else{
				r = mid - 1;
			}
		}
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值