11091 最优自然数分解问题(贪心)

来源:互联网 发布:windows 9 编辑:程序博客网 时间:2024/06/02 11:37

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)等于02)max{m1*m2*...*mk} = 4*3^[(n-4)/3]  if n(mod 3)等于13)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;}


0 0