P1249 最大乘积

题目描述

一个正整数一般可以分为几个互不相同的自然数的和,如 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 51+4=2+3 6 = 1 + 5 = 2 + 4 6=1+5=2+4 6=1+52+4

现在你的任务是将指定的正整数 n n n 分解成若干个互不相同的自然数(也可以不分解,就是这个数字本身)的和,且使这些自然数的乘积最大。

输入格式

只一个正整数 n n n,( 3 ≤ n ≤ 10000 3 \leq n \leq 10000 3n10000)。

输出格式

第一行是分解方案,相邻的数之间用一个空格分开,并且按由小到大的顺序。

第二行是最大的乘积。

样例 #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;
}
  • 24
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值