洛谷题单【算法1-6】二分查找与二分答案

关于二分算法:https://blog.csdn.net/weixin_43772166/article/details/105307762

P2249 【深基13.例1】查找

使用STL即可

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;

int a[N];

int main(void)
{
    
    
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%d", &a[i]);
	}
	int t;
	while (m--) {
    
    
		scanf("%d", &t);
		int idx;
		if (binary_search(a + 1, a + n + 1, t)) {
    
    
			idx = lower_bound(a + 1, a + n + 1, t) - a;
		} else {
    
    
			idx = -1;
		}
		printf("%d ", idx);
	}
	
	return 0;
}

P1102 A-B 数对

没有想出用二分的做法,用的map,需要开一下longlong

#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;

map<int, ll> mp;

int main(void)
{
    
    
	int n, c, t;
	cin >> n >> c;
	for (int i = 0; i < n; i++) {
    
    
		scanf("%d", &t);
		mp[t]++;
	}
	ll res = 0;
	for (auto item : mp) {
    
    
		int a = item.first;
		res += mp[a] * mp[a - c];
	}
	cout << res << endl;
	
	return 0;
}

P1873 砍树

check函数可能会爆掉int,所以需要每次都判断是否已经达到了m,如果达到了就返回true。

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;

int n, m, maxv;
int arr[N];

bool check(int h)
{
    
    
	int sum = 0;
	for (int i = 0; i < n; i++) {
    
    
		if (h < arr[i])
			sum += arr[i] - h;
		if (sum >= m)
			return true;
	}
	return false;
}

int main(void)
{
    
    
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
    
    
		scanf("%d", &arr[i]);
		maxv = max(maxv, arr[i]);
	}
	int l = 0, r = maxv;
	while (l < r) {
    
    
		int mid = (l + r + 1) / 2;
		if (check(mid))
			l = mid;
		else
			r = mid - 1;
	}
	cout << r << endl;
	
	return 0;
}

P1024 [NOIP2001 提高组] 一元三次方程求解

判断是否有零点时要写成 < 0 而不是 <= 0,因为当端点是零点时会判断两次

所以可以额外判断区间的左右端点

#include <iostream>
#include <cstdio>
#include <set>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;

double a, b, c, d;

double get(double x)
{
    
    
	return a*x*x*x + b*x*x + c*x + d;
} 

int main(void)
{
    
    
	cin >> a >> b >> c >> d;
	for (int i = -100; i <= 100; i++) {
    
    
		double l = i, r = i + 1;
		// 判断左端点是否是零点 
		if (get(l) == 0)
			printf("%.2f ", l);
		// 如果当前小区间有解,就进行二分 
		if (get(l) * get(r) < 0) {
    
    
			while (l + 1e-5 < r) {
    
       
				double mid = (l + r) / 2;
				if (get(l) * get(mid) < 0)
					r = mid;
				else
					l = mid;
			}
			printf("%.2f ", r);
		}
	}
	
	return 0;
}

P1678 烦恼的高考志愿

直接使用STL库即可

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;

int n, m;
int arr[N];

int main(void)
{
    
    
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
    
    
		scanf("%d", &arr[i]);
	}
	sort(arr, arr + n);
	int t, res = 0;
	while (m--) {
    
    
		scanf("%d", &t);
		int idx = lower_bound(arr, arr + n, t) - arr;
		if (idx == 0)
			res += abs(arr[0] - t);
		else if (idx == n)
			res += abs(arr[n - 1] - t);
		else
			res += min(abs(arr[idx - 1] - t), abs(arr[idx] - t));
	}
	cout << res << endl;
	
	return 0;
}

P2440 木材加工

常见的木棒分割问题

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;

int n, m, maxv = 0;
int arr[N];

bool check(int x)
{
    
    
	int sum = 0;
	for (int i = 0; i < n; i++) {
    
    
		sum += arr[i] / x;
	}
	return sum >= m;
}

int main(void)
{
    
    
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
    
    
		scanf("%d", &arr[i]);
		maxv = max(maxv, arr[i]);
	}
	int l = 0, r = maxv;
	while (l < r) {
    
    
		int mid = (l + r + 1) / 2;
		if (check(mid))
			l = mid;
		else
			r = mid - 1;
	}
	cout << r << endl;
	
	return 0;
}

P2678 [NOIP2015 提高组] 跳石头

求最大的最小距离

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;

int n, m, maxv = 0;
int arr[N];

bool check(int x)
{
    
    
	int sum = 0, now = 0;
	for (int i = 0; i <= n; i++) {
    
    
		if (arr[i + 1] - now >= x)
			now = arr[i + 1];
		else
			sum++;
	}
	return sum <= m;
}

int main(void)
{
    
    
	int L;
	cin >> L >> n >> m;
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%d", &arr[i]);
	}
	arr[n + 1] = L;
	int l = 0, r = L;
	while (l < r) {
    
    
		int mid = (l + r + 1) / 2;
		if (check(mid))
			l = mid;
		else
			r = mid - 1;
	}
	cout << r << endl;
	
	return 0;
}

P3853 [TJOI2007]路标设置

求最小的最大距离

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;

int n, m;
int arr[N];

bool check(int x)
{
    
    
	int sum = 0;
	for (int i = 0; i < n - 1; i++) {
    
    
		// 路标之间的距离 
		int len = arr[i + 1] - arr[i];
		if (len > x) {
    
    
			sum += len / x;
			// 如果 len 是 x 的倍数,需要减去一个
			if (len % x == 0)
				sum--;
		}
	}
	return sum <= m;
}

int main(void)
{
    
    
	int L;
	cin >> L >> n >> m;
	for (int i = 0; i < n; i++) {
    
    
		scanf("%d", &arr[i]);
	}
	int l = 0, r = L;
	while (l < r) {
    
    
		int mid = (l + r) / 2;
		if (check(mid))
			r = mid;
		else
			l = mid + 1;
	}
	
	cout << r << endl;
	
	return 0;
}

做这道题的时候一直有两个样例过不去,在题解后面看到了这样一段话,共勉

(下面这些话,大佬们不要喷,各个地区情况不一样,真的很多人在挣扎)
写这篇题解,真的只是纪念一下,我在计算机的路上经历了很多,去年csp之后正式退役,我受不了一次又一次打击和失败了,很久没有碰计算机,转物理竞赛,疫情期间,闲来无事,写了几个小代码,自动录录屏什么的,突然觉得尽管没得什么奖,但没白学;
我曾经学计算机是无比的功利,一心想拿奖,进队,保送,曾经觉得我放弃计算机是最为错误,现在发现真的没什么对错,计算机的路上已经教了我很多;
如果你正好看到了这篇题解,如果你是挣扎在计算机的道路上,我希望能帮到你,走过的这一路比任何奖项都珍贵,不要太紧张,慢慢走,会好的。

P1182 数列分段 Section II

需要注意的是 sum 应该初始化为1,表示整体是一段

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;

int n, m;
int arr[N];

bool check(int x)
{
    
    
	// 注意 sum 应该初始化为 1 
	int sum = 1, now = 0;
	for (int i = 0; i < n; i++) {
    
    
		// 如果当前数字比 x 还大,直接返回false 
		if (x < arr[i])
			return false;
		if (now + arr[i] <= x) {
    
    
			now += arr[i];
		} else {
    
    
			now = arr[i];
			sum++;
		}
	}
	return sum <= m;
}

int main(void)
{
    
    
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
    
    
		scanf("%d", &arr[i]);
	}
	int l = 0, r = 1e9;
	while (l < r) {
    
    
		int mid = (l + r) / 2;
		if (check(mid))
			r = mid;
		else
			l = mid + 1;
	}
	
	cout << r << endl;
	
	return 0;
}

P1163 银行贷款

需要知道每月的变化为剩余金额乘以利率,再减去当月的还款数,即:res * (1 + x/100) - b

如果最后 res > 0 说明税率过高,res < 0说明税率过低

最后上限 r 需要写大一点

#include <iostream>
#include <cstdio>
#include <set>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;

double a, b, c;

bool check(double x)
{
    
    
	double res = a;
	for (int i = 0; i < c; i++) {
    
    
		res = res * (1 + x / 100) - b;
	}
	return res >= 0;
}

int main(void)
{
    
    
	cin >> a >> b >> c;
	double l = 0, r = N;
	while (l + 1e-5 < r) {
    
    
		double mid = (l + r) / 2;
		if (check(mid))
			r = mid;
		else
			l = mid;
	}
	printf("%.1f\n", r);
	
	return 0;
}

P3743 kotori的设备

二分枚举设备可以坚持的时间,计算可以提供的能量和需要的能量

需要注意 r 应该设为1e10

#include <iostream>
#include <cstdio>
#include <set>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;

int n;
double p, a[N], b[N];

bool check(double x)
{
    
    
	// 可以提供的最多能量 
	double q = p * x;
	// 需要的能量总和 
	double sum = 0;
	for (int i = 0; i < n; i++) {
    
    
		if (a[i] * x <= b[i])
			continue;
		else
			sum += (a[i] * x - b[i]);
	}
	return sum <= q;
}

int main(void)
{
    
    
	double sum = 0;
	cin >> n >> p;
	for (int i = 0; i < n; i++) {
    
    
		scanf("%lf%lf", &a[i], &b[i]);
		sum += a[i];
	}
	if (sum <= p) {
    
    
		puts("-1");
	} else {
    
    
		double l = 0, r = 1e10;
		while (l + 1e-5 < r) {
    
    
			double mid = (l + r) / 2;
			if (check(mid))
				l = mid;
			else
				r = mid;
		}
		printf("%.4f\n", r);
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43772166/article/details/113817620