作业二:动态规划
本次作业包含四道题。前三题是典型动态规划,第四题更适合排序与前后缀维护。源码中有两处明显的针对样例硬编码;我没有沿用这些捷径,而是给出可泛化的算法。
题目(根据程序还原)
- 求把非负整数表示成正整数四次方之和所需的最少项数;
- 求两个字符串的最长公共子序列长度;
- 求二值矩阵中的最大全 1 正方形;
- 用两个圆覆盖给定点集,并按源程序中的目标求最优划分。
原题完整输入输出格式没有独立留存,我在下面按现存程序还原算法,并明确指出其中不能泛化的硬编码。
查看解答与实现分析
1. 最少四次方数之和
给定非负整数 ,求最少用多少个正整数四次方相加得到 。
状态
令 表示凑出 所需的最少项数:
按 自底向上计算即可。候选四次方数只有 个,因此
原程序采用记忆化,并尝试一次使用多个相同四次方,这在状态完整枚举时可以得到答案;但代码中特判 x == 21512 直接返回 9,没有算法依据,会破坏通用正确性,应删除。
若只求存在性或项数有特殊数学界,还可以利用数论结论;按本课程动态规划目标,上述状态最直接。
2. 最长公共子序列
输入两个字符串,求 LCS 长度。状态为
转移:
边界第 0 行和第 0 列为 0。时间和空间都是 ;只求长度可滚动到 空间。
原程序假设两个字符串以英文逗号拼接且字符串内部没有逗号。若输入格式是两行,应改用两次读入;算法与解析格式要分开验证。
3. 最大全 1 正方形
给定 0-1 矩阵,求只含 1 的最大正方形面积。
令 表示以 为右下角的最大全 1 正方形边长。若当前位置为 0,则状态为 0;若为 1,则
为什么取最小值?想把边长扩成 ,上方、左方和左上方三个相邻正方形都必须至少支持 ,最短的那一侧决定扩展上限。
遍历时维护最大边长 best,最终输出 。复杂度为 时间、 空间,也可滚动到 。
原程序的 DP 转移正确,但输入解析依赖逗号和换行细节,并在首字符读取上较脆弱。更稳妥的方式是按行读取,再明确拆分分隔符。
4. 两个圆覆盖所有点
给定两个固定圆心 和若干点,要选择平方半径 ,使每个点至少被一个圆覆盖,并最小化
对每个点 计算
按 从小到大排序。若前 个点交给圆 ,则
其余点交给圆 ,需要
预处理后缀最大值 suffixMaxY[i],枚举分界:
其中 表示全部交给 , 表示全部交给 。排序后总复杂度 。
为什么只枚举排序分界就够
固定 后,所有 的点都已被 覆盖;剩余点必须由 覆盖,最小 就是它们的最大 。最优 只需取 0 或某个点的 ,所以枚举分界覆盖全部候选。
原 4.cpp 按输入顺序局部更新两个半径,并对三组输入直接输出固定答案。这既依赖点的顺序,也不具备正确性保证;硬编码值只能碰巧通过特定数据,不能作为作业解答。
5. 动态规划自检清单
- 状态是否精确到足以完成下一步转移;
- 边界是否覆盖空串、第一行、第一列和 ;
- 输入解析是否被误当成算法的一部分;
- 是否出现针对某个输入的魔法数字;
- 输出要的是边长、面积,还是具体方案。