CF1066D 题解
CF1066D
题意
有 \(n\) 个物品和 \(m\) 个盒子,每个盒子最开始都是空的,大小为 \(k\)。物品依次编号为 \(1, 2 \dots n\),第 \(i\) 件物品的大小为 \(a_i\)。
现在需要把物品装进盒子里,做以下操作:取出一个空盒子,从左到右遍历物品,如果第 \(i\) 件物品可以放进当前盒子,就放进去,盒子大小减少 \(a_i\),否则,重复这个操作。
现在像打包尽可能多的物品,为了达到目标,可以丢弃最左边的一些物品。
请你求出最多可以装多少个物品。
思路
首先,这个题目有一个很重要的点:读清楚题目!!!
其实可以抽象成这样一个模型:将一个序列分成若干段,每一段的和要小于等于 \(k\),并且尽可能使得分出来的段数最小。
那么,假设丢弃了前 \(x\) 个物品,且剩下的物品可以放在 \(m\) 个盒子里,那么说明丢弃前 \(x + 1\) 个物品,剩下的物品也可以放在 \(m\) 个盒子里。
所以,这道题具有 单调性,可以二分,时间复杂度为 \(O(n \times \log n)\)。
但是,这道题目有没有时间复杂度更优的做法呢?当然有,贪心。
因为要丢弃的是一段前缀,那么我们能不能考虑倒着做呢?
既然要考虑倒着做,那么我们就得先证明出正着做和倒着做所得到的分段数是一样的。
假设序列的某一个后缀正着做的答案为 \(ans1\),倒着做的答案为 \(ans2\)。
- 先正着做,得到的答案为 \(ans1\),而再倒过来做不会使得原先的分界线向左移动,所以 \(ans2 \le ans1\)。
- 先倒着做,得到的答案为 \(ans2\),而再正过来做不会使得原先的分界线向右移动,所以 \(ans1 \le ans2\)。
所以,\(ans1 = ans2\)。