题目描述
一个正整数一般可以分为几个互不相同的自然数的和,如 3 = 1 + 2 3=1+2 3=1+2, 4 = 1 + 3 4=1+3 4=1+3, 5 = 1 + 4 = 2 + 3 5=1+4=2+3 5=1+4=2+3, 6 = 1 + 5 = 2 + 4 6=1+5=2+4 6=1+5=2+4。
现在你的任务是将指定的正整数 n n n 分解成若干个互不相同的自然数(也可以不分解,就是这个数字本身)的和,且使这些自然数的乘积最大。
输入格式
只一个正整数 n n n,( 3 ≤ n ≤ 10000 3 \leq n \leq 10000 3≤n≤10000)。
输出格式
第一行是分解方案,相邻的数之间用一个空格分开,并且按由小到大的顺序。
第二行是最大的乘积。
样例 #1
样例输入 #1
10
样例输出 #1
2 3 5
30
思路
思路1:贪心
思路参考大佬题解。看一下他的思路即可,代码感觉还是自己写的比较清晰。
至于贪心的证明嘛。。。有待补充。。。
思路2:动态规划
思路参考大佬题解。也是看他思路,代码看自己的,代码中注释也有思路讲解。
补充:为什么背包一定可以装满?
代码
1. 贪心
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#define endl '\n'
using namespace std;
const int N = 10010;
int n;
vector<int> s;// 存储序列
bool del[N];// 标记某个数是否在最后的序列中
vector<int> mul(vector<int>& a, int b) {
vector<int> c;
int t = 0;
for (int i = 0; i < a.size(); i++) {
t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
while (t) {
c.push_back(t % 10);
t /= 10;
}
while (c.size() > 1 && c.back() == 0) {
c.pop_back();
}
return c;
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
// 如果比5小,最优解就是本身
if (n <= 4) {
cout << n << endl << n << endl;
return 0;
}
// 从2开始求和,求到大于等于n
int sum = 0;
for (int i = 2; i <= n; i++) {
s.push_back(i);
sum += i;
if (sum >= n) {
break;
}
}
int k;// 总和sum与n的差值
if (sum > n) {
k = sum - n;
if (k == 1) {
// 如果差值为1,则去掉2,并将最后一个数+1,使得和刚好等于n
del[2] = true;
int x = s.back();
s.pop_back();
s.push_back(x + 1);
}
else {
// 如果差值为k,则去掉k
del[k] = true;
}
}
// 累乘
vector<int> ans = { 1 };
for (int i = 0; i < s.size(); i++) {
if (del[s[i]] != true) {
ans = mul(ans, s[i]);
}
}
// 打印序列
for (int i = 0; i < s.size(); i++) {
if (del[s[i]] != true) {
cout << s[i] << " ";
}
}
cout << endl;
// 打印乘积
for (int i = ans.size() - 1; i >= 0; i--) {
cout << ans[i];
}
cout << endl;
return 0;
}
2. 动态规划
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define endl '\n'
using namespace std;
const int N = 10010;
int n;
int v[N];// 每件物品的体积
double w[N];// 每件物品的收益
double f[N];// 所有状态
int pre[N];// 记录每个状态转移时的前驱,即每个f[]是由哪个f[]转移过来的,便于之后求每一步的选择,即分解的序列
vector<int> mul(vector<int>& a, int b) {
vector<int> c;
int t = 0;
for (int i = 0; i < a.size(); i++) {
t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
while (t) {
c.push_back(t % 10);
t /= 10;
}
while (c.size() > 1 && c.back() == 0) {
c.pop_back();
}
return c;
}
int main() {
cin >> n;
if (n <= 4) {
cout << n << endl << n << endl;
return 0;
}
// 01背包
// n件物品,背包容量为n,物品 i 的体积为 i,收益为 ln(i)
// 因为ln(a) + ln(b) + ... + ln(x) = ln(ab...x)
// 求出收益的最大值即求出了成绩的最大值,即他们所选择的数的方案是一样的
for (int i = 1; i < n; i++) {
v[i] = i;
w[i] = log(i);
}
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];
pre[j] = j - i;// 记录前驱
}
}
}
vector<int> s;// 所选数的序列
for (int i = n; i != 0; i = pre[i]) {
s.push_back(i - pre[i]);// 与前驱的差,即为该步选择的数
}
for (int i = s.size() - 1; i >= 0; i--) {
cout << s[i] << " ";
}
cout << endl;
vector<int> ans = { 1 };
for (int i = s.size() - 1; i >= 0; i--) {
ans = mul(ans, s[i]);
}
for (int i = ans.size() - 1; i >= 0; i--) {
cout << ans[i];
}
cout << endl;
return 0;
}