P2517 [HAOI2010] 订货
题意
你需要管理冰棍的买卖。具体来说,有 \(n\) 天,第 \(i\) 天冰棍有 \(d_i\) 的需求量,订购单价为 \(p_i\)。你需要在一天开始时订购冰棍,并且必须满足当天的所有需求。冰箱大小为 \(S\),每一根冰棍从第 \(i\) 天保存到第 \(i+1\) 天需要额外花费 \(m\) 的成本。如果冰棍当天就卖出了,则不需要支付保存成本。
问如何决策每天买入冰棍的数量,才能使总成本最小?
题解
费用流做法比较显然,这里讲一种单调栈维护反悔贪心的线性做法。
假设第 \(i\) 天购买了 \(x_i\) 根冰棍。保存的费用可以表示为
化简:
\(d_i\) 是常数,容易计算,暂不考虑。再给 \(x\) 加入订购单价 \(p_i\) 的贡献:
记 \(k_i=m(n-i+1)+p_i\);问题即转化为,在满足每天需求,并且不能超过冰箱容量的情况下,最小化
先考虑如何满足每天的需求。我们构造 \(\forall i,\ x_i=d_i\),这是一组可行解,但不一定最优。我们通过将一些冰棍的购买提前(把后面的 \(x_i\) 向前调整)实现最优化,同时能保证每天的需求被满足。
考虑贪心,我们从第 \(n\) 天枚举到第 \(1\) 天,假设当前已经得到了一个后缀 \([i+1,n]\) 的最优解。对于第 \(i\) 天,我们可以多买一些冰棍,替换掉 \([i+1,n]\) 中一些更劣(\(k_j\ge k_i\))的冰棍。
我们可以使用一个数据结构维护后缀中所有 \(x_j\ne 0\) 的 \(j\) 和对应的 \(k_j\),每次优先替换 \(k_j\) 最大的冰棍。
那我们如何处理冰箱容量的约束呢?首先,\(\forall i,\ x_i\le p_i+S\),否则当天的冰棍就已经不能装进冰箱了。我们能够证明,只需要满足这个条件,按照我们的贪心顺序求出的方案一定合法。
考虑 \([i+1,n]\) 中第一个 \(x_j\ne 0\) 的 \(j\),和第一个 \(x_t=p_t+S\) 的 \(t\)(冰箱满了,\(>t\) 的部分都无效),\(k_j\) 一定是闭区间 \([j,t]\) 中所有 \(x\ne 0\) 的位置中 \(k\) 最大的一个。否则将 \(k\) 更大(更劣)的那个位置上的冰棍都放到 \(j\) 这天购买,一定更优。
同理,我们可以证明闭区间 \([i+1,t]\) 中所有 \(x\ne 0\) 的位置,它们的 \(k\) 值单调递减。因此,第 \(i\) 天多出来的冰箱容量一定优先分配给 \([i+1,t]\) 中最靠左的 \(x\ne 0\) 的位置。
对于 \(x_j=0\) 的位置,它们不产生冰棍,只消耗冰棍,因此对库存是有利的,瓶颈不在于它们。
对于最左边的 \(x_j\ne 0\) 的 \(j\),若 \(k_i<k_j\),我们尝试将 \(i\) 的冰棍分配给它。考虑两种情况:
- 如果 \(i\) 的剩余容量 \(p_i+S-x_i\ge x_j\),\(i\) 可以把第 \(j\) 天的冰棍全都包下来,\(x_i'\leftarrow x_i+x_j\),\(x_j'\leftarrow 0\);
- 此时第 \(i\) 天冰箱的剩余容量不会超过原本第 \(j\) 天冰箱的剩余容量,因为 \(p_i+S-x_i-x_j\le p_j+S-x_j\)(\(p_i-x_i\le 0\le p_j\));
- 因此本次操作之后,即使第 \(i\) 天装满冰箱,等到第 \(j\) 天结束时冰箱也不会超载。
- 因为 \(x_j=0\),所以将 \(j\) 弹出数据结构,继续考虑下一个 \(x_j\ne 0\) 的 \(j\)。
- 如果 \(i\) 的剩余容量 \(p_i+S-x_i< x_j\),\(j\) 会把 \(i\) 的剩余容量消耗完。根据上面的论述,冰箱仍然不会超载。
分配冰棍不要求 \(x_j\ne p_j+S\),这种情况属于第 \(2\) 种情况,\(j\) 会把 \(i\) 的库存消耗完,不会增大 \(j\) 这天的冰箱负载,以后也一定不会发生。
因此,我们只需要使用一个单调栈维护第一个 \(x_j\ne 0\) 的位置即可。