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