Loading

「刷题记录」LOJ/一本通提高篇 二分算法

由于不会三分,所以有关三分的题就跳过了

[愤怒的牛]

题目传送门:愤怒的牛
思路:很经典的二分答案,与跳石头类似,二分每个牛棚之间的最短距离,枚举判断答案是否合法
代码

点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n, l, r, ans, m;
int pos[N], s[N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

int check(int x) {
	int dis = pos[1] + x, cnt = 1;
	for(int i = 2; i <= n; ++i) {
		if(pos[i] >= dis) {
			++cnt;
			dis = pos[i] + x;
		}
	}
	return cnt >= m;
}

int main() {
	n = read(), m = read();
	for (int i = 1; i <= n; ++i) {
		pos[i] = read();
	}
	sort (pos + 1, pos + n + 1);
	r = pos[n] - pos[1];
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(check(mid)) {
			ans = mid;
			l = mid + 1;
		}
		else {
			r = mid - 1;
		}
	}
	printf("%d\n", ans);
	return 0;
}

[Best Cow Fences]

题目传送门:Best Cow Fences
思路:依然是二分答案,但由于扯上了浮点数,所以还要维护精度,这样操作就麻烦了起来,二分出平均数,枚举,记录每个数与平均数的差值,求一个和,如果这个和 \(\ge\) 0,那么这个答案就是合法的,只要这个答案对一个区间合法即可,因为题目中说的是是否存在,有一个合法,那就是存在,其他就是精度处理问题了, 看看代码吧。
代码:

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
const double exp = 1e-7;
ll n, len;
double l, r;
double a[N], sum[N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

int check(double x) {
	sum[0] = 0;
	for(int i = 1; i <= n; ++i) {
		sum[i] = sum[i - 1] + a[i] - x;
	}
	double minn = 0x3f3f3f3f;
	for(int i = len; i <= n; ++i) {
		minn = min(minn, sum[i - len]);
		if(sum[i] >= minn)	return 1;
	}
	return 0;
}

int main() {
	n = read(), len = read();
	for (int i = 1; i <= n; ++i) {
		scanf("%lf", &a[i]);
	}
	double l = -1, r = 2e3 + 1;
	while(r - l > exp) {
		double mid = (l + r) / 2;
		if(check(mid)) {
			l = mid;
		}
		else {
			r = mid;
		}
	}
	printf("%d",int(r * 1000));
	return 0;
}

[数列分段 II]

题目传送门:数列分段 II
思路:其实和 数列分段I 相比就多了个二分答案,判断过程都是一样的,直接上代码:
代码:

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
ll n, m;
ll l, r, ans, all;
ll a[N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

int check(ll x) {
	ll sum = 0, cnt = 1;
	for (int i = 1; i <= n; ++i) {
		if(a[i] > x)	return 0;
		if(sum + a[i] > x) {
			sum = 0;
			++cnt;
		}
		sum += a[i];
	}
	return cnt <= m;
}

int main() {
	n = read(), m = read();
	l = 1e12;
	for (int i = 1; i <= n; ++i) {
		a[i] = read();
		all += a[i];
		l = min(l, a[i]);
	}
	r = all;
	while(l < r) {
		ll mid = (l + r) >> 1;
		if(check(mid)) {
			ans = mid;
			r = mid;
		}
		else {
			l = mid + 1;
		}
	}
	printf("%lld\n", ans);
	return 0;
}

[扩散]

题目传送门:扩散
思路:这里有一个规律,一个点如果在第 \(i\) 分钟之前被扩散到,那它的横坐标 \(x\) ,与纵坐标 \(y\) 相加一定不超过 \(i\)
那么,两个点之间的距离如果不超过 \(2 \times i\) ,那么这两个点就可以在 \(i\) 分钟内形成连通块,然后二分答案,二分出时间,按照上面的方法判断就行了
代码:

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N = 60;
int n, l, r, ans;
int x[N], y[N], fa[N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

int find(int g) {
	return fa[g] == g ? fa[g] : fa[g] = find(fa[g]);
}

int check(int mid) {
	for(int i = 1; i <= n; ++i) {
		fa[i] = i;
	}
	for(int i = 1; i < n; ++i) {
		for(int j = i + 1; j <= n; ++j) {
			int dis = abs(x[i] - x[j]) + abs(y[i] - y[j]);
			if(dis <= (mid << 1)) {
				int xx = find(i), yy = find(j);
				fa[xx] = yy;
			}
		}
	}
	int cnt = 0;
	for(int i = 1; i <= n; ++i) {
		if(fa[i] == i)	++cnt;
	}
	return cnt == 1;
}

int main() {
	n = read();
	for(int i = 1; i <= n; ++i) {
		x[i] = read(), y[i] = read();
	}
	l = 1, r = 1e9;
	while(l < r) {
		int mid = (l + r) >> 1;
		if(check(mid)) {
			ans = mid;
			r = mid;
		}
		else {
			l = mid + 1;
		}
	}
	printf("%d\n", ans);
	return 0;
}

结束

写到这才发现二分的题目全都是二分答案,三分等学了之后再补,准备进入搜索部分!

posted @ 2022-08-13 08:40  yi_fan0305  阅读(30)  评论(0编辑  收藏  举报