码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2014-12-03 19:13:40      阅读:183      评论:0      收藏:0      [点我收藏+]

标签:贪心   算法   编程   c++   

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;
}


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

标签:贪心   算法   编程   c++   

原文地址:http://blog.csdn.net/u013571487/article/details/41700689

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!