枚举答案(二分答案)

目录

算法简介:

题目

0. 臭皮匠

题目描述

解答

1. 臭皮匠加强版

题目描述

解答

优化:可行性的单调性

1.5. 一点点的小结:

2. 跳石头

题目描述:

解答:

3. 人工智能开关

题目描述:

输入样例:

输出样例 :

 解答:

 4. 送礼物就要体面

题目描述:

输入样例:

输出样例:

解答:

5. 差距中位数

题目描述:

 解答:

最后的总结

结束:


算法简介:

其实就是先从答案出发,再判断可不可行。当然,如果\!『』有单调性的话,还可以二分

至于这个『』是什么,请听后文分析。

注:本文章主要将思路,以及解题过程,模板题会穿插代码,剩下的只会给思路

题目

0. 臭皮匠

题目描述

n 个臭皮匠坐成⼀排,从左数第 i 个人智商为 x_i 他们将分组挑战诸葛亮。每⼀组只可以安排相就坐的若干个臭皮匠上场挑战,必须保证每组的总智商不低于m,求最多派出几组臭皮匠?

解答

注:不用看,我没有打错标题,这题只是一个引入。先把给文章的主题(二分答案)抛却脑后。

首先,看到这种题,想到的就是……

没错,我们伟大的切钢条问题

状态:f_i= 前 i 个人最多能派出几组

决策:上一组的最后位置 k

转移方程:f_i = \max_{j = 0}^{i - 1}(f_j|\sum_{k = j + 1}^{i} \geq m)

这个时候,我们发现:

  1.  f 本身单调不降 (f_i\leq f_{i + 1})
  2. 求和符号(约束条件)是单调降

所以,递推式变成了这样得:

f_i = max (j | \sum_{k = j + 1}^{i} x_k \geq m)

变成了贪心。

代码自然长这样:

int cnt = 0, sum = 0;
for (int i = 0; i < n; i ++) {
    sum += x [i];
    if (sum > = m) {
        sum = 0;
        cnt ++;
    }
}
cout << cnt;

1. 臭皮匠加强版

题目描述

n 个臭皮匠坐成⼀排,从左数第 i 个人智商为 x_i 他们将分组挑战诸葛亮。每⼀组只可以安排相就坐的若干个臭皮匠上场挑战,必须安排 k 组,求诸葛亮得智商最大可以为多少

解答

思路:转化为已知问题

不知道诸葛亮得智商🤔
简单粗暴:不知道就枚举

于是我们从大到小枚举诸葛亮得智商。

然后,用上一题的贪心法进行判断:诸葛亮的智商是否能派出 k 组

复杂度:O_{(mn)}

优化:可行性的单调性

我们知道:OK_{(m )} = 如果诸葛亮智商为 m 是否能派出 k 组。

显然:如果OK_{(m )} = true,则OK_{(m - 1)} = true

所以,OK 函数有可行性的单调性,再 true 和 false 之间有一个分割点

即:

1111000000

所以,我们可以用二分法对 m 进行枚举,称为二分答案

复杂度:O_{(n\: log_m)}

#include <bits/stdc++.h>
using namespace std;
int n, k, x [10000];
bool OK (int tmp) { 
	int cnt = 0, sum = 0;
	for (int i = 0; i < n; i ++) {
	    sum += x [i];
	    if (sum > = tmp) {
	        sum = 0;
	        cnt ++;
	    }
	}
	if (cnt >= k) return true;
	else return false;
} 
int main () {
	cin >> n >> k;
	int sum = 0;
	for (int i = 0; i < n; i ++) {
		cin >> x [i];
		sum += x [i];
	}
	sum /= k;
	int left = 0, right = sum;
	while (left < right) {
		int mid = (left + right + 1) / 2;
		if (OK (mid)) left = mid;
		else right = mid - 1;
	}
	cout << left;
}

好了,这道题就这样了。

1.5. 一点点的小结:

首先,我们知道了『』代表了 可行性

其次……

2. 跳石头

题目描述:

⼀条笔直的河道中分布着⼀些岩石。已选好两块岩石作为起点和终点,间距为 L。起点和终点之间 n 块岩石。选手们将从起点出发,每步跳向相邻的岩石,直至终点。为了提高比赛难度组委会计划移走 m 块岩石,使选手们在比赛过程中的最短跳跃距离尽可能长(m\leqslant n\leqslant 5\ast 10^4, L\leqslant 10^9)

解答:

  • 难点:优化目标是⼀个全局量(最小值的最大值)

                \min_{i = 1}^{n} c_i \geqslant v \Rightarrow c_i \geqslant v (1\leqslant i\leqslant n)

                那这样的优点是什么呢:我们把集体约束拆成了单个的约束

这种题就是套路,只需要枚举这个 v 就可以了,再进行判断。

那么,我们要写的 OK 函数的定义就是:使得相邻间隔全部都 \geqslant v 可不可行。

仔细看一眼👁️,这道题 其实就是上一道题……

OK 函数给一下:

bool OK (int v) {
    int cnt = 0, lst = 0;
    for (int i = 0; i <= n + 1; i ++) {
        if (x [i] - x [lst] < v) cnt ++;
        else lst = i;
    }
    return cnt <= m;
}

3. 人工智能开关

题目描述:

n 个灯泡排一直线,开始时有某些灯开着。每次操作能且只能让恰好连续的 k 个灯泡进行一次开关(开着的灯会关掉,关着的灯会打开)。一旦 k 设定好就不能改变。求 k 为多少,可用最少的操作次数使灯全部关闭。如有多个 k 使次数相同,则输出最小的 k 。如无解输出 -1。 (n\leqslant 5000)

输入样例:

1101011

输出样例 :

3

 解答:

首先,如果我们不知道 k ,根本做不了这道题。

不知道 k🤔
简单粗暴:不知道就枚举

那么 OK 函数怎么写:

  • 显然,同一段至多操作一次
  • 每次找到最靠左的开着的灯 i ,操作i+k- 1(别无选择)
  • 最后 k-1 盏灯决定了可行性

然后,很多读者开始帅气地写起了二分答案……

BUT, 这题的可行性没有单调性。

所以,这告诉我们,不能盲目地写二分,先判断单调性。

 4. 送礼物就要体面

题目描述:

送礼讲究“体面”,所以很多礼品包装都非常漂亮。而你的送礼哲学是“体面”体现在“体积”。从 n 个礼品里你需要挑选 k 个,第 i 件的体积是 v_i ,价格是 p_i 。请问对于选出的k件礼品,总体积除以总价格最大是多少?保留两位小数。(1\leqslant k\leqslant n\leqslant 10^5)

输入样例:

3 2
2 2
3 5
1 2

输出样例:

0.75

解答:

首先,我们要了解一个场景:最优性价比场景

  1. 错误❌的贪心法
  • 计算每件礼品的性价比:v_i / p_i
  • 按照单件性价比排序
  • 选性价比最高的 k 件

原因:体积大的会拖累小的。

那应该选怎样的物品呢,仔细看:

\frac{\sum_{i\in s}^{}v_i}{\sum_{i\in s}^{}p_i} > s \Rightarrow \sum_{i\in s}^{}(v _i - x*p_i) \geqslant 0

那么,我们要选择的,就是v _i - x*p_i最大的数

所以,判断依旧是 O_{(n)} 的复杂度。

然后,再枚举答案,最终复杂度:O_{(n log_n)}

5. 差距中位数

题目描述:

输入 n 个数 x_1, x_2 .........x_n ,求两两之差的中位数。(n\leqslant 10^5)

输入样例:

4

1 2 4 7

输出样例:

3

 解答:

首先,我们分析得到:

共有 C_{ n}^{ 2} 个差值,其中第 \left \lfloor C_{n}^{2} /2 \right \rfloor 的值

这种题叫做 k优化问题 

设 C_{(d)} 代表差值\leqslant d 的有几对 

ans = min_{C_{(d)}\geqslant k}^{} d

又∵ C_{(d)} 单调不降,所以很明显,可以二分。

那么,C_{(d)} 怎么写呢?

如果暴力枚举肯定超时:

C_{(d)} = \sum_{1\leqslant j < k\leqslant n}^{} [|x_k - x_j|\leqslant d]

我们发现如果排序,不会对结果产生影响

所以,我们排序之后去绝对值得

C_{(d)} = \sum_{j = 1}^{n - 1}\sum_{k = j + 1}^{n} [x_k \leqslant x_j+ d]

问题转化为:已知 j ,求不超过 x_k + d 的最大的  x_k

这里,k 也可以二分

最终复杂度:O_{(nlog_n nlog_An)}

当然,也可以优化到O_{(nlog_n + nlog_A)}

最后的总结

结束:

好了,以上就是本期文章的全部内容了,也感谢你能读到这里。最后,求赞求收藏求评论求转发,最重要的是点一个大大的关注。你们的支持就是我写文章的最大动力,我们下期再见!

  • 29
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值