这题很多细节。二分的 平均数 中位数 相邻必选1 的问题。。都是技巧。。满满的细节。。。。
1 平均数 二分的一个方法。。
验证的技巧 比如选3个
(x+y+z)/3>=mid
x+y+z>=mid*3;
x-mid +y-mid + z-mid>=0 …
这样无论选多少个。。我们实际上是无所谓的。。我们只要把所选的-mid的值相加就行了。。
这样就不需要讨论 要选多少个 的问题。
2 中位数 有一个技巧就是把a[i]值 转化成 1 和-1 的贡献值。。
这样一个好处是方便计算。。然后 又可以避免对 n的奇偶性进行讨论🔥
都是统一的
n为偶数 1234 。中位数是2 那么>=2的就有3个 3>1…
n为奇数 12345 。。总位数3 。那么>=3的就3个 3>2 …
所以我们用a[i]>=mid?1:-1 然后验证的时候f[n]>=1作为判断。。非常舒服。
这里为什么用a[i]>=mid 而不是<=mid 。。至于原因?
我的理解是 如果<=mid n奇偶的时候判定的条件不一样。。1234偶数>=就可以。12345奇数要严格大于>。这样判定条件就不好统一。
有更好理解的可以评论区指教下。
以前我是转化成0 和 1 。但是感觉1 和-1 更方便。。他可以无视n的奇偶性性。。
3 相邻至少选一个的问题(不是相邻只能选一个)。。怎么处理比较优雅,这题刚开始写的时候写的非常凌乱 各种关系非常不清晰。
和普通的dp的定义 和拿答案的位置 都有所不同
1 定义f[i] 是当前i位置必选!不是可选可不选 。。
2 答案在f[n-1]和f[n]之间。选优 因为n可以不选。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll __int128_t
#define ar array<int, 2>
#define arr array<int, 3>
int n, m, k, inf = 1LL << 61, mod = 998244353;// 1e9+7;
const int N = 5e5 + 50;
void solve() {
cin >> n;
int a[n + 1];
for (int i = 1; i <= n; ++i)
cin >> a[i];
double l = 0, r = inf;
for (int c = 0; c < 200; ++c) {
double mid = (l + r) / 2;
vector<double>f(n + 1, -inf);//f[i]定义是 i位置必选‼️
f[0] = 0;
for (int i = 1; i <= n; ++i) {
double x = 1.0 * a[i] - mid;
f[i] = max(f[i], f[i - 1] + x);//都选
if (i > 1)
f[i] = max(f[i], x + f[i - 2]);//隔开一个
}
if (f[n - 1] >= 0 || f[n] >= 0)// 因为n可以不选的。。所以答案有两个位置
l = mid;
else
r = mid;
}
cout << l << '\n';
{
// 下面这些只是复制了一份 改了下逻辑而已 。也可以封装成一个函数 不过我选择不封装 这里不封装 逻辑更直白清晰。
//这边因为 我习惯用这些变量名l r之类的 下面要重新声明 大括号下可以重新定义
int l = 0, r = inf;
while (l < r) {
int mid = (l + r + 1) / 2;
vector<int>f(n + 1, -inf);//f[i]定义是 i位置必选‼️
f[0] = 0;
for (int i = 1; i <= n; ++i) {
int x = a[i] >= mid ? 1 : -1;
f[i] = max(f[i], f[i - 1] + x);//都选
if (i > 1)
f[i] = max(f[i], x + f[i - 2]);//隔开一个
}
if (f[n - 1] >= 1 || f[n] >= 1)// 因为n可以不选的。。所以答案有两个位置
l = mid;
else
r = mid - 1;
}
cout << l << " ";
}
};
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout << fixed << setprecision(15);
#ifdef DEBUG
freopen("../1.in", "r", stdin);
#endif
//init_f();
//init();
//expr();
// int T; cin >> T; while(T--)
solve();
return 0;
}
。。。这题解有个小插曲。。刚开始的题目贴错了。。因为页面同时开着几道题。但是也没注意。不过竟然还有人点赞了。感谢那位雷锋。。哈哈。
碰到了中位数的另外一个类型题。
D - Pond
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll __int128_t
#define ar array<int, 2>
#define arr array<int, 3>
int n, m, k, inf = 1LL << 61, mod = 998244353;// 1e9+7;
const int N = 1150;
int a[N][N], sum[N][N];
void solve() {
cin >> n >> k;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
cin >> a[i][j];
auto ok = [&](int t) {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
sum[i][j] = -sum[i - 1][j - 1] + sum[i][j - 1] + sum[i - 1][j] + (a[i][j] >= t ? 1 : -1);//但是至少这里是统一了
if (i >= k && j >= k) {
int cnt = sum[i][j] - sum[i - k][j] - sum[i][j - k] + sum[i - k][j - k];
if (cnt < 1)//他最小的都要符合。。所以一旦有不符合 就是说明不行。
return 0;
}
}
return 1;
};
int l = 0, r = inf;
while (l < r) {
int mid = (l + r + 1) >> 1;//但是这里找的是最小值。。我们用的是右边界??
if (ok(mid) )
l = mid ;
else
r = mid - 1 ;
}
cout << l;
};
// 就是一个二分。。然后 1 -1 转换。。
//顺应天性 快乐洒脱
//感谢 有这平台机会
//感谢 自己的健康
//感谢 这么多好题 思路first
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout << fixed << setprecision(15);
#ifdef DEBUG
freopen("../1.in", "r", stdin);
#endif
//init_f();
//init();
//expr();
// int T; cin >> T; while(T--)
solve();
return 0;
}