记 f i f_i fi 为 i i i 时刻发车,处理完等车时间 ≤ i \le i ≤i 的学生的最小花费。
线性 dp。考虑枚举分界点 j j j,将 j + 1 j+1 j+1 到 i i i 的学生打包在 i i i 时刻送走,花费为 ∑ i − t x \sum i - t_x ∑i−tx,其中 t x t_x tx 在 j + 1 j+1 j+1 到 i i i 之间。时间复杂度 O ( T 2 ) O(T^2) O(T2)。代码
注意到 j + 1 j+1 j+1 到 i i i 超过 2 m 2m 2m 可以再次分段。 时间复杂度 O ( m T ) O(mT) O(mT)。代码
一开始我还想了一种错误的解法:记 f i f_i fi 表示处理完前 i i i 个人的最小花费,记 g i g_i gi 表示处理完前 i i i 个人的到站时间,我们贪心的让 g g g 越小越好。代码。错误之处在于 不具有最优子结构。具体来说,考虑到 g g g,最优的 f f f 并不一定能推出最优解。
用废了 i i i 个人,领队体力为 h e a d head head,跑了 t o t tot tot 圈。每次决策换不换领队。代码
注意: 应当先判断不合法在判断边界。
此题另一种做法可以参考 我同学的博客,两者大体思路差不多 。
对于前 50% 的数据,设计 f ( i , j , k ) f(i,j,k) f(i,j,k) 表示位数为 i i i、用 j j j 根火柴、余数为 k k k 的最优答案。答案在 k = 0 k=0 k=0 中找最大 即可。时间复杂度 O ( n 2 m ) O(n^2m) O(n2m)。 代码
分析超时原因,考虑将其中一维转成收益。设计 f ( i , j ) f(i,j) f(i,j) 表示 位数为 i i i、余数为 j j j 的最小花费火柴数量。容易求出最终答案的位数 l e n len len。
统计出 l e n len len 后,考虑从高位往低位从大往小填数。由于需满足位数,我们应当保证填完第 i i i 位后, i i i 的低位仍然存在合法方案。
具体地,假设第 i i i 位填了 d d d、 i i i 的高位填了 c u r s u m cursum cursum 根火柴、 i i i 的高位余数为 c u r m o d curmod curmod。可推出 i i i 的低位余数 k k k:
c u r m o d × 1 0 i + d × 1 0 i − 1 + k ≡ 0 ( m o d m ) curmod \times 10^i + d \times 10^{i-1} + k \equiv 0 \pmod m curmod×10i+d×10i−1+k≡0(modm)
k ≡ − c u r m o d × 1 0 i − d × 1 0 i − 1 ( m o d m ) k \equiv - curmod \times 10^i - d \times 10^{i-1}\pmod m k≡−curmod×10i−d×10i−1(modm)
则 d d d 应满足 c u r s u m + a d + f ( i − 1 , k ) ≤ n cursum + a_d + f(i-1,k) \le n cursum+ad+f(i−1,k)≤n。
预处理 10 10 10 的幂次即可,时间复杂度 O ( n m ) O(nm) O(nm)。
代码
总结:状态转收益从而降维。
状态转收益。代码。
第一问比较容易设计 f ( i , j , k ) f(i,j,k) f(i,j,k) 表示三个快递员分别在 j , k , p i j,k,p_i j,k,pi 的最小花费。
第二问要求在较小的空间限制输出方案。我们考虑根据最终的答案状态推出所有时刻的状态。考虑每一步决策推到上一步决策的过程,朴素方式是记录两个位置,事实上只需要记录转移到 p i p_i pi 的位置即可,该过程因递推过程的状态转移而异。实现可以使用 unsigned char
。
在推出每次决策的位置后。又因初始位置已知,我们容易得到任意时刻快递员的变化情况。
代码
简单题。状态转收益。代码
一开始的错误做法:记录三个状态, 2 , 5 , 10 2,5,10 2,5,10 分别最多。这样是有后效性的,即第一关键字相等情况下选 2 , 5 2,5 2,5 取决于后续状态。
f ( i , j ) f(i,j) f(i,j) 表示前 i i i 个,A 比 B 多 j j j 的最大取值。代码
记搜 O ( n 2 2 n ) O(n^22^n) O(n22n) 跑过去了。代码
如果是国际象棋棋盘,发现一块骨牌覆盖一个黑点和一个白点。然后就是二分图的最大匹配。
代码
数位 dp。需要注意前导零带来的影响。
代码
如果考虑 0 0 0,子集和是对称出现的。子集和 可重集 的中位数,相当于在子集和的 不重集 里找中间数。
代码
完全背包。 f ( i ) f(i) f(i) 表示和为 i i i 的方案数。
考虑到一维的时候顺序任意,不妨令 a i a_i ai 为最后一个数。 f ( t ) − f ( t − a i ) f(t) - f(t-a_i) f(t)−f(t−ai) 即可。
代码
上一题的 01 背包版本。考虑将最后一轮倒序撤销贡献即可。
代码