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

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

11091 最优自然数分解问题(贪心)的相关文章

把一个100以内的自然数分解因数。大小端的判断。

写一个程序,把一个100以内的自然数分解因数.(自然数分解因数就是将一个自然数分解为几个素数的乘积,提示,由于该数不是很大,所以可以将质数保存在数组中,以加快计算速度) 1 #include<stdio.h> 2 #include<math.h> 3 int Count(int n) 4 { 5 int i = 2; 6 for(i = 2;i<=sqrt(n);i++) 7 { 8 if(n%i==0) 9 { 10 printf("%d*",i); 1

【BZOJ-4514】数字配对 最大费用最大流 + 质因数分解 + 二分图 + 贪心 + 线性筛

4514: [Sdoi2016]数字配对 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 726  Solved: 309[Submit][Status][Discuss] Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对. 在

【20181031】一串数字【分解质因数+贪心】

题面 [错解] 立方就是所有质因子次数都是3的倍数嘛 发现1e5的三次根很小,可以枚举所有和这个数乘起来是完全立方数的(flag*1) 然后--连条边跑最大独立集? 不对啊是NP问题(实际上是个二分图) 那多半要优化连边变成一棵树(flag*2) 推了0.5h没一点结果,就暴搜,希望能剪点枝(那么大的数据剪个*的枝) 然后--搜挂了!0pts [正解] 既然只和%3有关,那我们可以分解质因数时直接%掉 这样和一个数配对的数是唯一的 由于有重复的数(%了之后),我们可以把它们合并.如果原来是完全立

最优装载问题_贪心

一.  问题描述    有一批集装箱要装上一艘载重为C的轮船.其中集装箱i的重量为Wi.最优装载问题要去确定在装载体积不受限制的情况下,将极可能多的集装箱装上轮船. 二.  解题思路及所选算法策略的可行性分析 使用贪心算法. 问题描述为: max∑Xi,∑WiXi<=C,Xi∈{0,1}   1<=i<=n 其中,变量Xi=0表示不装入集装箱i,Xi=1表示装入集装箱i. 贪心选择性质: 设集装箱已依其重量从小到大排序,(X1,X2,…,Xn)是最优装问题的一个最优解.又设k=min{i

贪心算法----正整数分解问题 和相同,乘积最大

一.问题描述 设n是一个正整数.现在要求将n分解为若干个自然数之和,且使这些自然数的乘积最大. 本文将这个大问题分解为两个小问题: (1)这些自然数是互不相同的 (2)这些自然数可以是相同的 二.解决思路 这其实是个数学问题,总体上的宗旨就是分解的数越接近,它们的乘积是最大的,而且不要分解出1,至少从2开始. 针对(1)这个问题,因为这些若干个自然数是不相同的,所以只能是从2开始分解,如2,3,4...这种顺序 在这里,我们把这个分解问题具体化: 9=2+3+4,刚好可以分解这三个连续的数: 1

贪心算法

一,贪心算法的设计思想 ? 从问题的某一个初始解出发逐步逼近给定的目标,每一步都作一个不可回溯的决策,尽可能地求得最好的解.当达到某算法中的某一步不需要再继续前进时,算法停止. 二,贪心算法的基本性质 1)贪心选择性质 所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到.这是贪心算法可行的第一个基本要素,也是贪心法与动态规划法的主要区别. 2) 最优子结构性质 该问题解的整体最优性依赖于其局部子问题解的最优性.这种性质是可以采用贪心算法解决问题的关键特征.例如

算法复习笔记(分治法、动态规划、贪心算法)

分治法 动态规划 贪心算法 分治法 分治法的基本思想是将一个规模为n的问题分解为k个规模较小的问题,这些子问题互相独立且与原问题相同(所以可以递归).递归地解这些子问题,然后将各个子问题的解合并得到原问题的解.它的一般算法设计模式如下: divide-and-conquer(P) { //|P|表示问题的规模,n0表示阈值,当规模不超过n0时,问题容易解出,不必分解 if(|P|<=n0) adhoc(P); //将P分解成子问题 divide P into smaller subinstanc

贪心算法和动态规划的区别与联系

联系 1.都是一种推导算法 2.都是分解成子问题来求解,都需要具有最优子结构 区别 1.贪心:每一步的最优解一定包含上一步的最优解,上一步之前的最优解则不作保留: 动态规划:全局最优解中一定包含某个局部最优解,但不一定包含前一个局部最优解,因此需要记录之前的所有的局部最优解 2.贪心:如果把所有的子问题看成一棵树的话,贪心从根出发,每次向下遍历最优子树即可(通常这个"最优"都是基于当前情况下显而易见的"最优"):这样的话,就不需要知道一个节点的所有子树情况,于是构不

算法导论——lec 13 贪心算法与图上算法

之前我们介绍了用动态规划的方法来解决一些最优化的问题.但对于有些最优化问题来说,用动态规划就是"高射炮打蚊子",采用一些更加简单有效的方法就可以解决.贪心算法就是其中之一.贪心算法是使所做的选择看起来是当前最佳的,期望通过所做的局部最优选择来产生一个全局最优解. 一. 活动选择问题 [问题]对几个互相竞争的活动进行调度:活动集合S = {a1, a2, ..., an},它们都要求以独占的方式使用某一公共资源(如教室),每个活动ai有一个开始时间si和结束时间fi ,且0 ≤ si &