11091 最优自然数分解问题
时间限制:1000MS 内存限制:65535K
提交次数:0 通过次数:0
题型: 编程题 语言: C++;C;VC;JAVA
Description
问题描述:设n是一个正整数。 (1)现在将n分解为若干个互不相同的自然数之和,且使这些自然数的乘积最大。 (2)现在将n分解为若干个自然数之和,且使这些自然数的乘积最大。 编程任务:对于给定的正整数n,编程计算问题(1)和(2)的最优分解的最大乘积。 注意: 1. 这里的自然数不含0但允许为1。 2. 特别地,当整数n无法分解为若干互不相同的加数时,即自身视为单独的一个加数,比如输入2,问题(1)的解输出为2。 而如果整数n可以分解为若干互不相同的加数时,不考虑自身为单独加数的情况,比如4,问题(1)的解输出为3,而非4。 3. 若干互不相同自然数或若干自然数,这个若干可>=1,也就是可以为1。
输入格式
只有一个正整数n(1<=n<=100)。
输出格式
输出待解问题(1)和(2)的最大乘积,中间空格相连,这两个数可能较大请皆用64位整数。 如,输入n为10,若加数互不相同,则n=2+3+5,此时最大乘积为2*3*5=30。 若加数可相同,则n=2+2+3+3,此时最大乘积为2*2*3*3=36。
输入样例
10
输出样例
30 36
提示
分析: 注意无论是(1)还是(2),乘积皆用64位整数表示。 (1)分解为互不相同自然数之和 注意到: 若a+b等于一个常数,则|a-b|越小,ab就越大。 要使得加数互不相同,又尽可能集中,那加数只能是连续的自然数了。 贪心策略:将n分成从2开始的连续的自然数的和。如果最后剩下一个数,将此剩余数在后项优先的方式下均匀地分给前面各项。 (2)分解为若干自然数之和 注意到: 若a+b等于一个常数,则|a-b|越小,ab就越大。 若 n = m1+m2+...+mk,则 -1 <= (mi-mj) <= 1,(1<=i<=k, 1<=j<=k),即任意加数的差距不超过正负1。 由于拆分的加数可以相同,任何一个数拆后乘积总比不拆强,因此拆到极尽,极尽的加数为3或2,且拆为3比拆为2好,因此优先拆为3。 贪心策略: 极尽拆解,尽可能先将n拆成3,3,3,...,3;若拆成若干3后还有剩余,则为2,或2和2。 归纳公式如下: 1)max{m1*m2*...*mk} = 3^(n/3) if n(mod 3)等于0 2)max{m1*m2*...*mk} = 4*3^[(n-4)/3] if n(mod 3)等于1 3)max{m1*m2*...*mk} = 2*3^[(n-2)/3] if n(mod 3)等于2 另外此题所涉的64位整数,如何使用? 编译环境不同,对64位整数的定义和输入输出略有不同: 1) gnu gcc/g++ 中long long类型,或unsigned long long, 输入输出用cin和cout直接输出,用scanf和printf也可以的(但本OJ系统不支持!)。 long long a; cin >> a; cout << a; 也可以使用:(注意一下,本OJ系统的gcc/g++不支持64位整数以"%I64d"形式输出, 但标准gnu gcc是支持如下的,在codeblocks上可以无误运行) long long a; scanf("%I64d",&a); printf("%I64d",a); 2) VC中用__int64类型,或unsigned __int64 __int64 a; scanf("%I64d",&a); printf("%I64d",a); vc下,64整数不要用cin和cout来输入输出,据说vc下64位整数兼容不好,会出错!大家可测试一下如下程序在vc下是否会出错? __int64 a; cin >> a; cout << a;
作者
zhengchan
我的实现代码:
#include <iostream> #include <math.h> using namespace std; /* 测试数据: 1 2 3 4 10 30 */ int main() { int n; cin >> n; if(n == 1 || n == 2) { // 排除n=1,2的情况 cout << n << " " << n; }else{ // 互不相同自然数之和 long long _res1 = 1; if (n == 3 || n == 4) { // 排除n=3,4的情况 _res1 = n - 1; }else{ int _count = 0; int _rest = 0,_avg = 0,_mod = 0; int a[n + 1];// a[0] 和 a[1]未使用 for(int i = 2; i <= n; i++){ // 初始化a a[i] = 1; } for(int i = 2; i <= n; i++) { _count += i; if(_count > n) { _rest = n - (_count - i);// 多出来的部分 if (_rest < (i - 2)) {// 不够每个已选的数都分到1 _avg = 1; _mod = 0; }else{// 每个已选的数至少能分到1 _avg = _rest / (i - 2);// 平均分个前i - 2个已确定的数 _mod = _rest % (i - 2);// 平均分后剩下的 } for(int j = i - 1; j >= i - _rest; j--){ // 从后往前分配多出来的部分 a[j] += _avg; } a[i-1] += _mod;//最后一位+_mod break; } a[i] = i; } for(int i = 2; i <= n; i++) { _res1 *= a[i]; } } //若干自然数之和 /* 归纳公式如下: 1)max{m1*m2*...*mk} = 3^(n/3) if n(mod 3)等于0 2)max{m1*m2*...*mk} = 4*3^[(n-4)/3] if n(mod 3)等于1 3)max{m1*m2*...*mk} = 2*3^[(n-2)/3] if n(mod 3)等于2 */ long long _res2 = 1; if(n % 3 == 0){ _res2 = pow(3,n/3); }else if(n % 3 == 1){ _res2 = 4*pow(3,(n-4)/3); }else{ _res2 = 2*pow(3,(n-2)/3); } cout << _res1 << " " << _res2; } cout << endl; return 0; }
时间: 2024-11-10 18:02:34