题目大意
给出一个正整数 n n n ,将 n n n 分解成若干个互不相同的自然数的和,且使这些自然数的乘积最大。
解题思路
方法一
按背包的思路来考虑,有 1 − n 1-n 1−n这些数,每个数选或不选,背包的容量是 n n n。最终价值是相乘求得,可以使用对数运算将乘法变为加法,且最终要求的是背包容量恰好为 n n n 的情况,并打印解。
#include <bits/stdc++.h>
using namespace std;
#define ENDL "\n"
typedef long long ll;
const int Mod = 1e9 + 7;
const int maxn = 1e4 + 10;
const double dinf = 1e100;
class BigNum {
//大数模板省略
}
int path[maxn];
double f[maxn], w[maxn];
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++) w[i] = log(i), f[i] = -dinf;
f[0] = 0;
for (int i = 1; i <= n; i++)
for (int j = n; j >= i; j--)
if (f[j - i] + w[i] > f[j]) {
f[j] = f[j - i] + w[i];
path[j] = i;
}
set<int> s;
for (int i = path[n]; i; n -= i, i = path[n]) s.insert(i);
BigNum ans(1);
for (auto i : s) ans = ans * i, cout << i << " ";
cout << endl;
ans.print();
return 0;
}
方法二
打表可以发现,将一个数分为若干个连续的自然数时,最终答案是最大的。于是我们从2开始不断对自然数进行拆分,当剩余的数不足以凑成下一个自然数时,将它分为若干个1,从已经分好的连续自然数最后开始依次向前加一,这个方法待证明。
下面代码的写法实际上和上述描述是等价的。
#include <bits/stdc++.h>
using namespace std;
#define ENDL "\n"
typedef long long ll;
const int Mod = 1e9 + 7;
const int maxn = 1e4 + 10;
vector<int> prime;
bitset<maxn> vis;
void euler() {
for (int i = 2; i < maxn; i++) {
if (!vis[i]) prime.push_back(i);
for (int j = 0; j < prime.size() && i * prime[j] < maxn; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
class BigNum {
//大数模板省略
}
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
euler();
int n;
cin >> n;
int sum = 0;
set<int> s;
for (int i = 2;; i++) {
sum += i;
s.insert(i);
if (sum >= n) break;
}
int res = sum - n;
if (res) {
if (res == 1)
s.erase(2);
else
s.erase(res);
}
BigNum ans(1);
for (auto i : s) ans = ans * i, cout << i << " ";
cout << endl;
ans.print();
return 0;
}