IOI稻米仓库选址问题:滑动窗口与加权中位数应用
发布时间:2026/11/3 13:07:34 锦皓数字建站

1. 项目概述IOI稻米仓库选址问题解析这道来自2011年国际信息学奥林匹克竞赛IOI的P5844题目本质上是一个经典的仓库选址优化问题。题目描述了一个直线上的稻米农场分布情景给定L块稻田的坐标位置x1, x2,..., xL按升序排列要求选定一个最佳位置建立米仓使得总运输成本不超过预算B的前提下能够服务尽可能多的农场。问题的核心在于理解运输成本的计算方式——题目规定将稻米从第i块稻田运到米仓的成本等于两者距离乘以该稻田的产量ri。这里就涉及到了算法中常见的加权中位数概念。与普通的中位数问题不同加权中位数需要考虑每个数据点的权重值这使得直接套用标准库函数变得不可行必须手动实现优化算法。在实际竞赛中这类题目往往考察选手对以下几个关键点的把握如何将现实问题抽象为数学模型对有序数据的处理能力滑动窗口等优化技巧的应用边界条件的全面考虑提示IOI题目通常会在基础算法之上增加一层现实问题的包装解题时需要先剥离表面描述识别出核心算法模型。2. 算法思路分析与选择2.1 暴力解法与复杂度分析最直观的解法是枚举所有可能的农场区间[i,j]计算在每个区间内建立米仓的最小成本然后找出满足成本约束的最大区间。对于每个区间计算最优米仓位置实际上就是求加权中位数的过程。假设有L块稻田这种暴力方法的复杂度是外层循环枚举区间起点O(L)内层循环枚举区间终点O(L)每个区间内计算加权中位数和总成本O(L) 总复杂度达到O(L³)对于L1e5的大数据量显然不可行。2.2 滑动窗口优化思路观察到当区间[i,j]满足成本约束时任何子区间 i,k 也一定满足这提示我们可以使用滑动窗口技术来优化。具体步骤初始化窗口左右边界leftright1尝试扩展右边界计算窗口内最优成本当成本超过B时移动左边界记录满足条件的最大窗口大小关键在于步骤2的高效实现——需要找到一种方法能够快速计算窗口内任意[i,j]区间的最优米仓位置和对应成本。2.3 前缀和加速计算为了将每次窗口移动后的成本计算优化到O(1)我们需要预先计算以下前缀和数组sum_r[i]前i块稻田的产量之和sum_xr[i]前i块稻田位置与产量的乘积之和sum_x2r[i]前i块稻田位置平方与产量的乘积之和利用这些前缀和数组当米仓建在位置x时总成本可以表示为 总成本 x²sum_r - 2xsum_xr sum_x2r这个二次函数的最小值点就是最优米仓位置x sum_xr / sum_r。将这个x代回成本公式就能得到最小总成本。3. C实现详解3.1 数据结构设计首先定义需要使用的数据结构和全局变量#include iostream #include vector #include algorithm using namespace std; typedef long long ll; // 避免整数溢出 vectorll x, r; vectorll sum_r, sum_xr, sum_x2r; int L; ll B;3.2 前缀和数组预处理在main函数中读取输入后立即计算三个前缀和数组int main() { cin L B; x.resize(L1); r.resize(L1); sum_r.resize(L1); sum_xr.resize(L1); sum_x2r.resize(L1); for(int i1; iL; i) { cin x[i] r[i]; sum_r[i] sum_r[i-1] r[i]; sum_xr[i] sum_xr[i-1] x[i]*r[i]; sum_x2r[i] sum_x2r[i-1] x[i]*x[i]*r[i]; } // 滑动窗口算法实现... }3.3 滑动窗口实现实现滑动窗口算法维护当前窗口[i,j]int max_hub 0; int left 1; for(int right1; rightL; right) { // 计算当前窗口[left,right]的最优成本和米仓位置 ll total_r sum_r[right] - sum_r[left-1]; ll total_xr sum_xr[right] - sum_xr[left-1]; ll total_x2r sum_x2r[right] - sum_x2r[left-1]; ll optimal_x total_xr / total_r; ll cost optimal_x*optimal_x*total_r - 2*optimal_x*total_xr total_x2r; while(cost B) { left; // 重新计算新的窗口参数 total_r sum_r[right] - sum_r[left-1]; total_xr sum_xr[right] - sum_xr[left-1]; total_x2r sum_x2r[right] - sum_x2r[left-1]; optimal_x total_xr / total_r; cost optimal_x*optimal_x*total_r - 2*optimal_x*total_xr total_x2r; } max_hub max(max_hub, right-left1); } cout max_hub endl;3.4 边界条件处理在实际编码中有几个边界条件需要特别注意当leftright时应该立即终止循环整数除法带来的精度问题可能导致成本计算偏差大数据量的输入输出效率使用ios::sync_with_stdio优化4. 算法优化与性能分析4.1 时间复杂度优化通过滑动窗口和前缀和技术的结合我们将算法复杂度从O(L³)优化到了O(L)这是因为外层循环遍历每个右边界O(L)每个右边界对应的左边界只会向右移动总共移动不超过L次O(L)每次窗口移动的成本计算是O(1) 因此总复杂度为O(L)能够轻松处理L1e5规模的数据。4.2 空间复杂度分析算法使用了三个长度为L1的前缀和数组空间复杂度为O(L)在常规竞赛环境中完全可接受。4.3 数值稳定性考虑由于题目涉及大量乘法运算需要注意使用long long类型避免整数溢出在计算optimal_x时整数除法会向下取整可能导致不是理论最优解可以尝试optimal_x附近几个整数值取成本最小的一个5. 测试用例设计与验证5.1 样例输入15 20 1 1 2 2 3 3 4 4 5 5预期输出3 解释选择[3,4,5]号农场米仓建在4成本13041*58205.2 样例输入23 10 1 5 2 1 3 2预期输出2 解释选择[1,2]号农场米仓建在1成本05111105.3 边界测试用例1 100 50 10预期输出1 验证程序对单点输入的处理能力6. 常见错误与调试技巧6.1 整数溢出问题这是此类题目最常见的错误。即使使用了long long在计算cost时仍然可能溢出特别是当x和r都很大时。解决方法检查所有中间结果是否可能溢出可以尝试提前约分计算公式使用更大的整数类型如int128如果环境支持6.2 滑动窗口移动条件错误常见错误包括移动左边界时没有重新计算窗口参数窗口移动方向错误没有正确处理leftright的情况调试技巧打印窗口左右边界和当前成本对小规模数据手动模拟算法过程6.3 精度问题由于使用整数除法计算optimal_x可能导致不是真正的最优解。解决方法检查optimal_x和optimal_x1两个位置的成本使用浮点数计算但要注意比较时的精度误差7. 算法扩展与变种思考7.1 二维平面上的仓库选址如果稻田分布在二维平面上问题将变得更加复杂。此时加权中位数概念扩展到二维成本计算需要考虑二维距离可能需要使用不同的优化策略7.2 动态农场情境如果农场的产量会随时间变化需要设计数据结构支持动态更新线段树等数据结构可能派上用场每次查询的最优解需要重新计算7.3 多仓库选址问题当可以建立多个仓库时问题转化为聚类问题可能需要使用动态规划或贪心算法复杂度会显著增加在实际刷题过程中我建议先彻底理解这个基础版本再尝试这些扩展变种。每次解决一个变种问题后都要思考它与原问题的异同点这样才能真正掌握算法设计的核心思想。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。