「刷题记录」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;
}
结束
写到这才发现二分的题目全都是二分答案,三分等学了之后再补,准备进入搜索部分!
朝气蓬勃 后生可畏