瓶子倒水二分法:最大化最小值
一、题目描述(核心背景与要求)
1. 题目背景
小蓝有n 个装水的瓶子,从左到右依次摆放,第 i 个瓶子装有 aᵢ 单位的水;为了美观,瓶子按 k 种颜色循环染色(第 i 个与第 i+k 个颜色相同)。
2. 操作规则
同颜色组内,若满足i < j(左瓶下标小于右瓶),可将任意整数单位的水从 i 倒入 j;
操作次数不限,水的总量保持不变(水守恒);
不同颜色组的瓶子,无法互相倒水(组间独立)。
3. 核心问题
二、题目拆解(规则+目标+核心限制)
1. 核心规则拆解——分组逻辑
由“k种颜色循环染色”可推出:瓶子按「下标对k取余」分成 k 个独立组,分组方式如下:
第 0 组:下标为 0, k, 2k, 3k, ...(对应题目第 1、k+1、2k+1 个瓶子)
第 1 组:下标为 1, k+1, 2k+1, ...
... ...
第 k-1 组:下标为 k-1, 2k-1, 3k-1, ...
重点标注:组间完全独立,水不能跨组流动,因此可对每个组单独分析,再整合结果。 |
2. 倒水规则的核心限制
只能左倒右:右瓶可接收左瓶的水,左瓶无法接收右瓶的水;
左瓶水量上限:左瓶的最终水量 ≤ 初始水量(无法从右侧获取水,最多保留自身水量);
右瓶水量无上限:右瓶可接收同组所有左侧瓶子的水,只要组内总水量足够。
3. 目标拆解
题目目标是「最大化所有瓶子的最小值」,这类问题是算法中经典的「最大化最小值题型」,此类题型有固定的最优解题思路——二分答案法。
三、解题思路推导(从题型到方法)
1. 为什么优先选择「二分答案法」?
「最大化最小值」题型的核心特性:答案具有单调性,具体表现为:
若某个值 x 可行(能让所有瓶子水量 ≥ x),则所有小于 x 的值都一定可行;
若某个值 x 不可行(无法让所有瓶子水量 ≥ x),则所有大于 x 的值都一定不可行。
这种“可行/不可行”的分界特性,恰好适配二分法的核心逻辑——通过不断缩小区间,找到最大的可行值。
2. 其他方法对比(为什么不选)
方法 | 优势 | 劣势 | 是否选用 |
暴力枚举 | 逻辑简单 | x 最大可达 1e12,超时严重 | 否 |
贪心算法 | 效率较高 | 需复杂分配逻辑,易出错 | 否 |
二分答案法 | 逻辑清晰、效率高(仅需40次左右判断) | 需设计判断函数 | 是(最优解) |
3. 二分答案法的整体框架
核心框架(必记):
|
四、核心函数设计(check(x)详解)
1. check(x) 函数的作用
给定一个目标值 x,判断:经过任意次合法操作后,能否让所有瓶子的水量都 ≥ x。
由于组间独立,只需判断每个组是否满足条件,所有组满足则 x 可行,否则不可行。
2. 单个组的可行条件(核心重点)
设某组的总水量为 sum,瓶子数量为 cnt,要让组内所有瓶子水量 ≥ x,需同时满足两个条件:
条件1:组内总水量足够(水守恒)
sum ≥ x * cnt
重点标注:若组内总水量不足以给每个瓶子分配 x 单位水,无论怎么倒水,都无法让所有瓶子达标,直接判定不可行。 |
条件2:组内左侧瓶子初始水量足够
从左到右遍历组内瓶子,直到遇到第一个初始水量 < x 的瓶子为止:
该瓶子左侧的所有瓶子,初始水量必须 ≥ x;
原因:左侧瓶子无法从右侧获取水,初始水量 < x 则永远无法达到 x;
右侧瓶子可通过左侧倒水补足 x,无需再检查。
3. check(x) 函数逻辑梳理
遍历每一个颜色组;
对每个组,先判断条件1(sum ≥ x*cnt),不满足则返回 false;
再判断条件2(左侧瓶子初始水量 ≥ x),不满足则返回 false;
所有组均满足条件,返回 true。
五、整体流程与样例验证
1. 整体解题流程(步骤化)
分组计算:遍历 k 个组,计算每个组的总水量 sum 和瓶子数量 cnt;
初始化二分边界:l=0,r=所有瓶子总水量 / n;
二分迭代:计算 mid,调用 check(mid),调整 l 和 r,记录最大可行值;
输出最大可行值,即为最终答案。
2. 样例验证(直观理解)
步骤1:分组计算
组0:[8, 2, 4] → sum=14,cnt=3
组1:[5, 2] → sum=7,cnt=2
组2:[5, 3] → sum=8,cnt=2
总水量=29,右边界 r=29/7≈4。
步骤2:二分验证
试 mid=3:
组0:14 ≥ 3*3=9(条件1满足);左侧8≥3,遇到2<3停止(条件2满足);
组1:7 ≥ 3*2=6(条件1满足);左侧5≥3,遇到2<3停止(条件2满足);
组2:8 ≥ 3*2=6(条件1满足);5≥3、3≥3(条件2满足);
→ mid=3 可行,尝试更大值(l=4)。
试 mid=4:
组1:7 ≥ 4*2=8(条件1不满足);
→ mid=4 不可行,尝试更小值(r=3)。
步骤3:最终答案
循环结束,最大可行值为 3,即答案为 3。
六、完整代码实现(可直接运行)
重点标注:代码采用函数式结构,分工清晰;使用long long 避免溢出,适配题目数据范围。 |
#include <stdio.h> // 数组最大长度(适配 n≤1e6 的题目要求) // 全局变量(简化参数传递,新手易理解) // 核心判断函数:判断能否让所有瓶子水量 ≥ x // 主逻辑函数:二分查找最大可行值 // 2. 初始化二分边界 long long ans = 0;// 存储最终答案 // 主函数:负责输入输出,调用核心逻辑 |
七、思路总结与同类题技巧
1. 完整思路总结(必记)
核心思考链:
|
2. 同类题解题技巧
看到「最大化最小值」「最小化最大值」,直接优先考虑二分答案法;
二分法的核心是「判断函数」,需结合题目规则设计可行条件;
涉及大数值(如总水量≥1e12),必须用 long long 避免溢出;
分组问题优先考虑「组间独立」,单独分析每组再整合结果。
3. 常见易错点提醒
忘记左瓶水量上限,忽略 check(x) 的条件2;
用 int 存储总水量,导致溢出;
二分边界设置错误(右边界未设为总水量/n);
分组逻辑错误(未按「i += k」遍历组内瓶子)。
