二分专项练习(一)

文章提供了四个编程竞赛题目,分别涉及二分查找算法的应用。题目一关注书本排列以减少宽度,题目二探讨顶点连接和等比数列,题目三解决雕刻师傅的工作时间优化,题目四处理速度匹配问题,所有题目均采用二分查找策略求解最佳解。
摘要由CSDN通过智能技术生成

习题一:

题目传送门:

https://codeforces.com/gym/103688/problem/A

题意:

有n本高度为a的A类数和m本高度为b的B类书,他们的厚度都是1,最初A类书都在左边,B类书都在右边,其宽度是n+m,选取k本B类书放置A类书的上面以缩短宽度,求最小宽度是多少。

思路:

二分答案

check函数中,将宽度x认为已知,判断其是否符合条件,若宽度为x,则拿出来的书就是n+m-x,再看这写书的放置是否合法即可。

代码显示如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 10;
int n,m,a,b,h;

bool check(int x)
{
	int p=n+m-x;
	int h1=h-a;
	if((n/b)*h1>=p) return true;
	p-=(n/b*h1);
	int h2=h-b;
	if((x+n%b-n)/b*h2>=p) return true;
	return false;
	
}

void solve() {
	
	cin>>a>>b>>n>>m>>h;
	int l=n+1,r=n+m;//注意k是[0,m-1]
	
	while(l<r)
	{
		int mid=(l+r)/2;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	cout<<l<<endl;
	
}

signed main() {
	int t = 1;
	cin>>t;
	while (t--) {
		solve();
	}
}

习题二:

题目传送门:

https://codeforces.com/contest/1846/problem/E2

题意:

有一个顶点,有k个新顶点与其相连接,可执行无限次以下操作:让最外的每个顶点通过边与k个新顶点相连接(此操作至少做一次),问是否存在k在执行上述操作后,使得顶点总和为n。(数据范围:1e18)

思路:遍历层数,等比数列求和,二分

首先按层数遍历,层数最小是三,有题意可知k最小值是2,以2为公比的等比数列的求和公式是(2^x-1)要小于等于1e18,此时这个x代表层数最大值,求得最大层数近似64,所以层数x的范围是3~64。接下来就是求对每个层数对应的顶点总和是否与n相等:

已知等比数列的求和公式是:

x即是对应层数,现在目的就是求k,若直接利用求和公式开根号求解会存在精度问题,所以为了得到准确k就需要进行二分。

代码显示如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 10;
int n, m, a, b, h;
//sum<=(q^x-1)/(q-1)->sum*(q-1)+1<=q^x
bool check(int x) {
	
	int l=2,r=1e9;
	while(l<=r)
	{
		int mid=(l+r)/2;
		__int128 a=(__int128)n*(mid-1)+1;//会爆int,用__int128
		__int128 q=1;
		for(int i=1;i<=x;i++)
		{
			q*=mid;
			if(q>a) break;
		}
		if(q==a) return true;
		
		if(q>a) r=mid-1;
		else l=mid+1;
		
	}
	return false;


}

void solve() {

	cin >> n;

	for (int i = 3; i <= 64; i++) {
		if (check(i)) {
			cout<<"YES"<<endl;
			return;
		}
	}
	cout<<"NO"<<endl;
}
signed main() {
	int t = 1;
	cin >> t;
	while (t--) {
		solve();
	}
}

习题三:

题目传送门:

https://codeforces.com/problemset/problem/1840/D

题意:

一个大小为n的数组,a[i]表示i个人想要的图案样式,有三个雕刻师傅,一个制作图案样式为y的师傅制作一个图案样式x需要的时间是|x-y|,雕刻师傅可以同时完成不同的图案,求顾客最佳最长等待时间。

题解:

本题主要查找最佳时间,所以二分的是时间,check函数中,假设时间是x,判断是否至多三个雕刻师让其最佳最长时间是x,此为二分边界。

代码显示如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n, m, a[N], b, h;
bool check(int x) {
	int peo=1;
	int m=a[1]+x;
	for(int i=2;i<=n;i++)
	{
		if(abs(a[i]-m)>x) peo++,m=a[i]+x;
	}
	return peo<=3;

}

void solve() {

	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	sort(a+1,a+1+n);
	int l=0,r=1e9;
	while(l<r){
		int mid=(l+r)/2;
		
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	cout<<l<<endl;

}
signed main() {
	int t = 1;
	cin >> t;
	while (t--) {
		solve();
	}
}

习题四:

题目传送门:

https://codeforces.com/gym/104417/problem/D

题意:

题解:

贪心+二分

根据题意我们可以知道,如果a队员的体重小于等于b队员的体重,则b队员背a队员时,其速度不变,若a队员的体重大于b队员的体重,则b队员的速度变为v[b]-(w[a]-w[b])=(v[b]+w[b])-w[a]。队员的速度取决与(v[b]+w[b])和-w[a],最佳速度匹配就是最大的(v[b]+w[b])与最小-w[a]相配,才能使整体速度达到最大,但是如何区分哪些队员是要被背起来,哪些队员是要背人的呢?这个分界线就是最大速度,这时就需要进行二分,假定最大速度是mid,那么速度大于等于mid的就是要背人的,即(v[b]+w[b]),速度小于mid的就是要被背的,即(-w[a]),将他们分别存到dn和up数组里,dn降序排序,up升序排序,最后两两相加看是否合法即可。

代码显示如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
int n, m, h;
typedef pair<int, int> PII;
PII a[N];
///体重小于or相等 v不变 体重大于 v[i]-(w[j]-w[i])=(v[i]+w[i])-w[j]
vector<int>up, dn;
bool check(int x) {
	up.clear(),dn.clear();
	for (int i = 1; i <= n; i++) {
		if (a[i].first >= x) dn.push_back(a[i].first + a[i].second);
		else up.push_back(-a[i].second);
	}
	if (up.size() > dn.size()) return false;

	sort(up.begin(), up.end());
	sort(dn.begin(), dn.end(), greater<int>());

	for (int i = 0; i < up.size(); i++) {
		if (up[i] + dn[i] < x) return false;
	}
	return true;

}

void solve() {
	cin >> n;

	for (int i = 1; i <= n; i++)
		cin >> a[i].first >> a[i].second;

	int l = 1, r = 1e9;
	while (l < r) {
		int mid = (l + r + 1) >> 1;
		if (check(mid)) l = mid;
		else r = mid - 1;
	}
	cout << l << endl;
}
signed main() {
	int t = 1;
	cin >> t;
	while (t--) {
		solve();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值