洛谷 P1249 最大乘积 题解
第一篇博客试水。
这是我几个月前做的一道黄题,当时洛谷上所有题解都只停留在粗浅地描述“感性”理解上,没有给出令人信服的严谨证明。于是,我花了半天时间,用更数学的语言写出这篇题解,希望能帮助到和我一样爱较真的同学。
Solution
设 \(n\) 的最佳分解方案是 \(n = a_1 + a_2 +\cdots + a_k\),其中 \(1\le a_1<a_2<\cdots<a_k\)。记 \(p = \prod_{i=1}^{k} a_i\) 为分解出自然数的最大乘积。
当 \(n \le 4\) 时,不难验证 \(n\) 的最佳分解方案就是不做分解,所以接下来我们只讨论 \(n \ge 5\) 时的情形。
首先,我们可以证明:
\(a_1\) 只可能是 \(2\)、\(3\) 或 \(4\)。【引理一】
证明 假设 \(a_1 \notin \{2,3,4\}\),对 \(a_1\) 分类讨论。
- 如果 \(a_1 > 4\) 且 \(a_1\) 是奇数,不妨设 \(a_1 = 2x+1, \ x\ge 2\),那么将 \(a_1\) 拆分为 \(x\) 和 \(x+1\),此时 \(p\) 变为原来的 \(\dfrac{x(x+1)}{2x+1}\)倍,这个倍数大于 \(1\),与 \(p\) 取最大值矛盾。
- 如果 \(a_1 > 4\) 且 \(a_1\) 是偶数,不妨设 \(a_1 = 2x, \ x\ge 3\),那么将 \(a_1\) 拆分为 \(x-1\) 和 \(x+1\),此时 \(p\) 变为原来的 \(\dfrac{(x-1)(x+1)}{2x}\)倍,这个倍数大于 \(1\),与 \(p\) 取最大值矛盾。
- 如果 \(a_1 =1\),那么删去 \(a_1\) 并把 \(a_k\) 加 \(1\),此时 \(p\) 变为原来的 \(\dfrac{a_{k}+1}{a_{k}}\) 倍,与 \(p\) 取最大值矛盾。
综上所述,假设不成立,原命题得证。\(\square\)
由于 \(a_1 < 5\) 且 \(n \ge 5\),\(n\) 一定会被分解为两个及以上的数,即 \(k \ge 2\)。接下来,我们可以证明:
任意相邻 \(a_i\) 之差 \(\in\{1,2\}\),且至多存在一对相邻 \(a_i\) 满足差为 \(2\)。【引理二】
证明 假设上述命题不成立,则一定存在两个正整数 \(x,y\) 同时满足以下三点:
- \(x,y \in [a_1,a_k]\)
- \(x\) 和 \(y\) 不是 \(\{a_i | i=1,2,\dots,k\}\) 中的数
- 存在 \(i_1,i_2 \in \{1,2,\cdots,k\}\),使得 \(a_{i_1} = x-1, \ a_{i_2} = y+1\)
于是,将 \(a_{i_1}\) 换为 \(x\),\(a_{i_2}\) 换为 \(y\),此时 \(p\) 变为原来的 \(\dfrac{xy}{(x-1)(y+1)}\) 倍,这个倍数大于 \(1\),与 \(p\) 取最大值矛盾,故原命题得证。\(\square\)
这个命题极大帮助我们确定最优解,它表明最佳的分解方案一定是一个连续或近乎连续(至多有一点不连续)的数列,这与我们的直觉是一致的。它还进一步缩小了 \(a_1\) 的范围:
\(a_1 \ne 4\)。【引理三】
证明 假设 \(a_1=4\),由引理二,\(a_2=5\) 或 \(6\)。
- 如果 \(a_2 = 5\),那么将 \(a_2\) 拆分为 \(2\) 和 \(3\),\(p\) 变为原来的 \(\dfrac{6}{5}\) 倍,这与 \(p\) 取最大值矛盾。
- 如果 \(a_2 = 6\),那么将 \(a_2\) 拆分为 \(2\) 和 \(3\),并把 \(a_k\) 加 \(1\),\(p\) 变为原来的 \(\dfrac{a_{k}+1}{a_{k}}\) 倍,这与 \(p\) 取最大值矛盾。
综上所述,假设不成立,故 \(a_1 \ne 4\)。\(\square\)
目前,我们已经确定了最佳分解方案一定首项为 \(2\) 或 \(3\),并且分解数列连续或近乎连续。通过下一条引理,我们可以继续缩小最优解范围:
如果 \(a_1 = 3\) 且存在一对相邻 \(a_i\) 满足差为 \(2\),那么这对相邻 \(a_i\) 必是 \(a_{k-1}\) 和 \(a_k\)。【引理四】
证明 设这对相邻 \(a_i\) 是 \(a_{t-1}\) 和 \(a_{t}\)。假设 \(t < k\),将 \(a_{t}\) 和 \(a_{t+1}\) 都自减 \(1\),并增添一项 \(2\),此时 \(p\) 变为原来的 \(\dfrac{2(a_{t}-1)(a_{t+1}-1)}{a_{t}a_{t+1}}\)倍,即 \(\dfrac{2(a_t-1)}{a_{t+1}}\) 倍,由于 $a_{t} > a_1 = 3 $,故这个倍数大于 \(1\),这与 \(p\) 取最大值矛盾,于是原命题得证。\(\square\)
根据上述四条引理,我们已经具备找到任意 \(n\) 的最佳分解方案的全部条件。
一方面,只有四种可能的最佳分解方案,分别是以 \(2\) 为首项的连续数列、以 \(2\) 为首项的近乎连续数列、以 \(3\) 为首项的连续数列和以 \(3\) 为首项的近乎连续数列。
另一方面,考虑一个给定的大于等于 \(5\) 的正整数 \(n\),可将其写成一个关于正整数 \(s\) 和 \(r\) 的式子 \(n = r + \sum_{i=2}^{s} i\),其中 \(r\) 和 \(s\) 都是唯一确定的且 \(0 \le r \le s\)。我们可以根据 \(r\) 的取值对 \(n\) 进行分类,其中每一类都对应一种最佳分解方案,具体而言:
| \(r\) 的取值 | 最佳分解方案 | 具体形式 |
|---|---|---|
| \(r = 0\) | 以 \(2\) 为首项的连续数列 | \(2 + 3 + \cdots + s\) |
| \(1 \le r \le s-2\) | 以 \(2\) 为首项的近乎连续数列 | \(2 + 3 + \cdots + (s-r) + (s-r+2) + (s-r+3) + \cdots+ (s+1)\) |
| \(r = s-1\) | 以 \(3\) 为首项的连续数列 | \(3 + 4 + \cdots + (s+1)\) |
| \(r = s\) | 以 \(3\) 为首项的近乎连续数列 | \(3 + 4 + \cdots + s + (s+2)\) |
由于每一种分解方案对应的 \(n\) 在 \(r\) 上也具有同样的共性,如以 \(2\) 为首项的连续数列对应的 \(n\) 的 \(r\) 均为 \(0\),因此这种对应关系是唯一的。至此,我们找到了任意 \(n\) 的最佳分解条件,问题解决。
Code
#include <bits/stdc++.h>
using namespace std;
struct BigDec {
int len, dec[10000];
void operator*=(int x) {
for (int i = 0; i < len; ++i)
dec[i] *= x;
int i = 0;
for (int carry = 0; i < len || carry; ++i) {
dec[i] += carry;
carry = dec[i] / 10;
dec[i] %= 10;
}
len = i;
}
void printDec() {
for (int i = len - 1; i >= 0; --i)
printf("%d", dec[i]);
}
} p;
int main() {
int n;
scanf("%d", &n);
if (n <= 4)
return printf("%d\n%d", n, n) & 0;
int s = (sqrt(8 * n + 9) - 1) / 2;
int r = n - (s - 1) * (s + 2) / 2;
p.dec[0] = p.len = 1;
if (r == 0) {
for (int i = 2; i <= s; ++i)
printf("%d ", i), p *= i;
} else if (r == s - 1) {
for (int i = 3; i <= s + 1; ++i)
printf("%d ", i), p *= i;
} else if (r == s) {
for (int i = 3; i <= s; ++i)
printf("%d ", i), p *= i;
printf("%d ", s + 2), p *= s + 2;
} else {
for (int i = 2; i <= s - r; ++i)
printf("%d ", i), p *= i;
for (int i = s - r + 2; i <= s + 1; ++i)
printf("%d ", i), p *= i;
}
printf("\n");
p.printDec();
return 0;
}

浙公网安备 33010602011771号