题解:学而思编程 乘积最大
【题目来源】
学而思编程:乘积最大
【题目描述】
有一个正整数 \(n\),将 \(n\) 的每一位数码拆开,分成 \(3\) 组,将每组内的数重新排列成 \(3\) 个新的正整数。(可以不按原数的顺序排列)
这 \(3\) 个新的正整数的乘积最大可能是多少?
【输入】
\(1\) 个正整数 \(n\)。
【输出】
输出最大乘积。
【输入样例】
123456
【输出样例】
136396
【核心思想】
-
问题分析:给定正整数 \(n\),将其各位数码拆开后分成 \(3\) 组,每组重新排列成一个新的正整数,要求这 \(3\) 个正整数的乘积最大。关键观察是:对于固定和的三个正整数,当它们尽可能接近时乘积最大(均值不等式)。因此策略是:将数码从大到小排序后,尽量让三个数的数值接近。
-
算法选择:
- 贪心分配:将排序后的数码循环分配到三个数中(大数码优先分配到当前最小的数)
- 微调平衡:通过交换操作使三个数的数值更加接近,最大化乘积
-
关键步骤:
- 数码分解与排序:将 \(n\) 的每一位存入数组 \(a\),从大到小排序
- 循环分配:对 \(i\) 从 \(1\) 到 \(n\):将 \(a[i]\) 分配到 \(v[(i-1) \bmod 3 + 1]\) 中
- 补零处理:
- 若 \(n \bmod 3 = 2\):给第三个数补一个 \(0\)(末尾加一个 \(0\) 位)
- 若 \(n \bmod 3 = 1\):给第二、三个数各补一个 \(0\)
- 平衡调整:
- 让最大的数(\(v[1]\))尽可能小:若 \(v[1]\) 和 \(v[3]\) 在某位相等,交换使 \(v[1]\) 更小
- 让后两个数的差值尽可能小:根据已确定的大小关系,交换数码使两数更接近
- 计算乘积:将三个向量转为整数,相乘后除以补零的影响因子
-
时间/空间复杂度:
- 时间复杂度:\(O(\log_{10} n)\),数码个数为 \(\log_{10} n\),排序和分配均为线性
- 空间复杂度:\(O(\log_{10} n)\),存储数码和三个数的各位
-
贪心平衡分配的核心思想:
- 均值不等式指导:对于固定和 \(S = a + b + c\),乘积 \(abc\) 在 \(a \approx b \approx c \approx S/3\) 时最大。因此分配策略的核心是让三个数的数值尽可能接近
- 循环分配的初始策略:将大数码依次分配到三个数中,确保每个数都能获得较大的高位数码,避免某个数过大
- 位数均衡:通过补零使三个数的位数相同(或接近),确保高位对齐比较和交换的有效性
- 局部交换优化:在循环分配后,通过逐位比较和交换,进一步缩小三个数之间的差距。特别是让最大的数尽量小、后两个数的差尽量小,是贪心策略的精细化调整
- 高位优先原则:交换操作从高位到低位进行,因为高位数码对数值大小的影响更大,优先调整高位能更有效地平衡三个数
- 适用于数码分配、资源均衡、乘积最大化等组合优化问题
【解题思路】

【算法标签】
贪心
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int a[20], n; // 存储数字的每一位
int cnt, b[20], zero = 1; // zero: 记录末尾0的个数
long long num; // 输入的数字
vector<int> v[5]; // 三个数字的每一位,v[1],v[2],v[3]存储三个数
int main()
{
cin >> num; // 输入数字
// 将数字分解为每一位
while (num) {
a[++n] = num % 10; // 从低位到高位存储
num /= 10;
}
// 从大到小排序
sort(a + 1, a + 1 + n, greater<int>());
// 将数字分配到三个数中(循环分配)
for (int i = 1; i <= n; i++) {
v[(i - 1) % 3 + 1].push_back(a[i]); // 循环分配到v[1],v[2],v[3]
}
// 处理长度不是3的倍数的情况
if (n % 3 == 2) { // 长度除以3余2
v[3].push_back(0); // 第三个数补0
zero = 10; // 记录有一个末尾0
}
if (n % 3 == 1) { // 长度除以3余1
v[2].push_back(0); // 第二个数补0
v[3].push_back(0); // 第三个数补0
zero = 100; // 记录有两个末尾0
}
bool f = 0; // 标记是否需要调整
bool f1 = 0; // 标记第一个数的最高位是否需要调整
// 检查第一个数的最高位和第三个数的最高位是否相等
if (v[1][0] == v[3][0])
f1 = 0; // 相等,不需要调整
else
f1 = 1; // 不相等,可能需要调整
// 处理:让第一个最大的数尽可能小
for (int i = 1; i < v[3].size(); i++) {
// 检查前一位是否已经不同
if (v[1][i - 1] != v[2][i - 1]) {
f = 1; // 已经不同
}
// 如果当前位v[1]和v[3]相等,继续
if (v[1][i] == v[3][i]) {
continue;
} else {
if (f1 == 0) { // 如果最高位相等
f1 = 1; // 标记已处理
continue;
} else {
swap(v[1][i], v[3][i]); // 交换,让v[1]更小
}
}
// 如果前一位相等且当前位不同,交换v[1]和v[2]
if (v[1][i - 1] == v[2][i - 1] && v[1][i] != v[2][i] && f == 0) {
swap(v[1][i], v[2][i]);
f = 1;
}
}
int t = -1; // 标记哪个数更大:2表示v[2]大,3表示v[3]大
// 让后两个数的差值尽可能小
for (int i = 1; i < v[3].size(); i++) {
if (t == -1) { // 第一次确定大小关系
if (v[2][i - 1] < v[3][i - 1]) {
t = 3; // v[3]更大
} else if (v[2][i - 1] > v[3][i - 1]) {
t = 2; // v[2]更大
}
}
if (t != -1) { // 已经确定大小关系
// 如果v[3]更大但当前位v[3]>v[2],交换让v[2]变大
if (t == 3 && v[3][i] > v[2][i])
swap(v[3][i], v[2][i]);
// 如果v[2]更大但当前位v[3]<v[2],交换让v[3]变大
else if (t == 2 && v[3][i] < v[2][i])
swap(v[3][i], v[2][i]);
}
}
long long x[5] = {0}, res = 1; // 计算三个数
for (int i = 1; i <= 3; i++) {
for (int j = 0; j < v[i].size(); j++) {
x[i] = x[i] * 10 + v[i][j]; // 构造数字
}
res *= x[i]; // 计算乘积
}
cout << res / zero; // 除以补0的影响
return 0;
}
【运行结果】
123456
136396
浙公网安备 33010602011771号