E - Average and Median 平均数 中位数 二分方式

这题很多细节。二分的 平均数 中位数 相邻必选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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值