数论:正整数分解使得乘积最大

时间:2025-04-16 09:04:48


引入问题:

要求将一个正整数n分成几个自然数的和,使这些自然数的乘积最大。输出这个最大值。
分两种要求:

(1)这些自然数可以相同;
(2)这些自然数互不相同;

(同一个数n,1的结果应该比2大)。



分析:
(只分析n>=4的情况,因为n<4最大值都是本身)

1.这些自然数可以相同

我们先列几个数找找规律:
4=2+2、5=3+2、6=3+3、7=3+2+2、8=2+2+2+2、9=3+3+3、10=2+2+2+2+2 …

观察这些数可以得如下规律:

(1)拆分的自然数不会超过3,因为4可以化为2+2,5可以化为3+2>5,所以所有的数都可以拆为只 有3和2的数;
(2)因为都可以拆除3和2,所有为了使乘积大,应该先尽可能拆除3,而后拆除2,分成1无贡献;


那么我们考虑所有的n除以3的情况:

  1. 可以被3整除,那么就将他全部拆为3,如:9=3+3+3;
  2. 被3除余1,那么可以拆为形如3+3+……+3+4,即3+3+……+3+2+2,如10=3+3+2+2;
  3. 被3除余2,那么可以拆为形如3+3+……+3+2,如11=3+3+3+2;

实现代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int t,n;
int main()
{
    scanf("%d",&t);
    while(T--)
    {
    	scanf("%d",&n);
        int ans;
        if(n<4) ans=n; 
        else
        {
          	int cnt3,cnt2; // 3的个数 2的个数
            if(n%2==1) // n为奇数
            {
                cnt3=(n-3)/6*2+1;
                cnt2=(n-3)%6/2;
            }
            else // n为偶数
            {
                cnt3=n/6;
                cnt2=n%6/2;
            }
            ans=pow(3,cnt3)*pow(2,cnt2);
        }
        printf("%d\n",ans);
    }
    return 0;
}



2.这些自然数互不相同

也是先写几个数找规律:
5=2+3、6=2+4、7=3+4、8=3+5、9=2+3+4、10=2+3+5、11=2+4+5、12=3+4+5、13=3+4+6、
14=2+3+4+5、15=2+3+4+6、16=2+3+5+6、17=2+4+5+6、18=3+4+5+6……
(其实不用列数字,凭以往经验也应该知道,如果互不相同,这些自然数应该尽量连续,才能使乘积大)

我们可以发现如下规律:

  • 数字应该尽量连续,而且应该从2开始(因为1不做贡献,分1不如把一加在后面的数上)

下面对连续自然数做出分析:
我们可以得到形如:2+3+4+5+……k的式子,n是任意整数,我们并不能才好得到连续的自然数,可能多出△x。
对应△x我们可以保证他0<=△x<=k(为什么一定小于等于k?因为如果大于k,原式应该多加一项变成2+3+4+……+k+k+1)

那么多出来的△x应该怎么处理,有上面列的几个式子的规律,显然应该从后往前均摊给前k-1个数。
为什么从后往前呢?应该从前往后或遇到重复的数。

那么当我们分完△x后,应该会得到两种式子:

  1. △x=k 3+4+5+6+……+k+(k+2)
  2. △x<k 2+3+4+……+(k-△x)+(k-△x+1)+……+(k+1)

显然我们要求出式子的值,应该借助阶乘,阶乘中缺失的项,我们除掉就好了,如果数值要求大要求取模,就要用到除法逆元了。求阶乘可以用前缀积,连续自然数则用前缀和,如果时间要求有现在,就用二分来优化,二分找最接近n的前缀和。


这里推荐一个题目:HDU 5976
(这题就用到了逆元,和二分)

题解