题解:P11246 [GESP202409 六级] 小杨和整数拆分
清新的动态规划。
易列出状态转移方程 dp[i] = min(dp[i], dp[i - j * j] + 1)。
解释在下面:
状态转移方程的含义
- 状态转移方程
dp[i] = min(dp[i], dp[i - j * j] + 1)(其中j * j <= i)的含义是在计算总和为i的完全平方数的最小数量dp[i]时,考虑使用小于等于i的完全平方数j * j来构建i。 dp[i - j * j]表示已经计算出的总和为i - j * j的完全平方数的最小数量。通过加上一个完全平方数j * j,就可以得到总和为i的一种拆分方式,其数量为dp[i - j * j] + 1(因为多使用了一个完全平方数j * j)。- 对于每个
i,我们尝试所有可能的完全平方数j * j(只要j * j <= i),并从这些可能的拆分方式中选择数量最少的,即取min(dp[i], dp[i - j * j] + 1)。这样不断更新dp[i],最终得到总和为i的完全平方数的最小数量。
例如,当计算 dp[5] 时:
- 首先
j = 1,j * j = 1,dp[5 - 1] = dp[4],假设dp[4]之前已经计算为1(因为4 = 4本身就是一个完全平方数,数量为1),那么dp[5]就会更新为min(dp[5], dp[4] + 1) = min(初始值, 1 + 1) = 2。 - 然后
j = 2,j * j = 4,dp[5 - 4] = dp[1],dp[1]初始为1(因为1 = 1),此时dp[5]会再次更新为min(dp[5], dp[1] + 1) = min(2, 1 + 1) = 2(因为2更小)。 - 继续
j = 3,j * j = 9,但9 > 5,循环结束,dp[5]最终为2,表示5可以拆分为1 + 4,使用了两个完全平方数,这是最少的拆分数量。
代码:
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int n;
cin >> n;
int dp[n + 1];
for (int i = 0; i <= n; i++) {
dp[i] = i; // 初始化,最坏情况就是全是1相加
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j * j <= i; j++) {
dp[i] = min(dp[i], dp[i - j * j] + 1);
}
}
cout << dp[n] << endl;
return 0;
}
可以的话,能不能点个关注和赞?