最大乘积
题目描述
一个正整数一般可以分为几个互不相同的自然数的和,如 \(3=1+2\),\(4=1+3\),\(5=1+4=2+3\),\(6=1+5=2+4\)。
现在你的任务是将指定的正整数 \(n\) 分解成若干个互不相同的自然数的和,且使这些自然数的乘积最大。
输入格式
只一个正整数 \(n\),(\(3 \leq n \leq 10000\))。
输出格式
第一行是分解方案,相邻的数之间用一个空格分开,并且按由小到大的顺序。
第二行是最大的乘积。
样例 #1
样例输入 #1
10
样例输出 #1
2 3 5
30
这道题是数论加高精度,高精度倒好说,就是高精度乘法实现,模拟列竖式乘法,但是找出要乘的这几个数实属不容易,没学过数论,只能从题解中学怎么找规律
这里引用一下洛谷题解区赞数最高的题解
点击查看代码
我来帮大家理清一下这题的思路,并对其他题解做出解释和补充(我之前看的时候也比较懵)。
以下是本题的主要思路:
大家都清楚这题的意思,我们尽可能的把n分成更多份(都大于1)那样乘积最大。 (这里讲一下如果分出来的数可以重复,那么有时候并不是分出的份数越多越大,比如6,分成2,2,2不如分成3,3。但是不能重复的话就不存在这样的情况,大家可以想一下。) 以“6”为例,我们可以想到,我们先分出来一个2,然后再分出一个3......但是这样面临一个问题:“最后有余数怎么办”,比如“8”;分出来2,3还剩下3,没法再分出4,这时候怎么办呢?有的同学会想我把余数3都加到3身上,我们就分成了2和6,但是这样是最大的吗?(3和5才是最大的) 这时候我补充一下有一篇题解中所说:
“把余数分到大的数上比分到小的数上得到的乘积更大”。
实际不太准确,我们很容易证明出来如果能把数分到更小的数上,那么乘积更大(大家可以想想)。但是为什么最终是分到了大的数呢?就好比把6分成2,4,按照我们刚才的分法,先分出来了2和3最后余1,理论上我们把1给2得到的结果更大,但是我们不允许数重复,所以我们需要先把1给3,这样如果还剩下余数的话就分给2,所以当小的数被分配某个数后不会造成数的重复,那么优先给小的数分配,所以就像其他题解所说:
“从大数开始向前,依次分配1”。
所以对于8我们先分配出了2,3又余3,我们先分配1给3,得到2,4这时候2可以被分配,那么我们就分配给2一个1,得到3和4,这时候还余1,我们就分配给4,得到3,5。
这里我来解释一下点赞数最多的题解的思路。
以下为引用部分:
本题要先用简单的数论和贪心找到最优解的组成方法,再用高精度乘法求积。
以2004为例,由于把2004分拆成若干个互不相等的自然数的和的分法只有有限种,因而一定存在一种分法,使得这些自然数的乘积最大。
若1作因数,则显然乘积不会最大。把2004分拆成若干个互不相等的自然数的和,因数个数越多,乘积越大。为了使因数个数尽可能地多,我们把2004分成2+3…+n直到和大于等于2004。
若和比2004大1,则因数个数至少减少1个,为了使乘积最大,应去掉最小的2,并将最后一个数(最大)加上1。
若和比2004大k(k≠1),则去掉等于k的那个数,便可使乘积最大。
例如15:s=2+3+4+5+6刚好大于15,s-15=5,所以把5去掉。
又例如13:s=2+3+4+5刚好大于13,s-13=1,所以去掉2,并把5加1,即3 4 6。
大家可能觉得这个和我们刚才所讲的不太一样,为什么要减呢?怎么想到的呢?显然这样减,比我们一个个循环加更快。
我来帮大家推导一下:
我们分出来了2,3,4......n这n-1个数然后余数是K(1<=K<=n)。
1.如果K==n,我们需要进行两轮分配,意思是从n分配到2还剩下1,需要再回去把1分配给最大的数(也就是n+1)最终得到3,4,5......n+2这也就应对了上文题解中的情况2;
2.如果K<n,我们进行一轮分配就好因为我们的余数是K,那么分配到n+1-K就停止了,拿15举例,先分配出了2,3,4,5余1,我们分配到5就停止了,(n+1-K=5)。 对于上篇题解他先分配出2,3,4......n,n+1。因为我们分出来n-1个数余数是K,而分配出n+1后会造成数不够,还差n+1-K,那么这也就对应前两行我们推导出的结果,去掉n+1-K,就相当于分配到了n+1-K,应为n+1-K经过分配后变成了,n+2-K,这不就相当于去掉了吗。所以这相当于找规律了。
对于数 N,从2开始2,3,4....n-1,n,k,保证从2到n是连续数字,最后余数为k,很显然1<=k<=n,对于k,我们考虑将其均分在2-n的数里
具体分法以及解释可以参考上述题解的描述
当k==n时,从2-n这n-1个数,都可以分到一个1,最终剩下n-n-1=1,再重复这个步骤,正好将这多的1分到最后一个数上
可以参考如图
下面是AC代码
点击查看代码
#include<iostream>
using namespace std;
int Num[10001];
int main()
{
int n;
cin >> n;
int Sum = 0;
int Cnt = 0;
//if (n < 5)
//{
// if (n == 3)
// printf("1 2\n2");
// if (n == 4)
// printf("1 3\n3");
//} 特判,小于5的数不太适合从2开始,不过数据没要求
// else
{
for (int i = 2;; i++)
{
Sum += i;
Num[Cnt++] = i;
if (Sum == n)
break;
else if (Sum > n)
{
if (Sum == n + 1)
{
Num[0] = 1;
Num[Cnt - 1] += 1;
}
else
{
Num[Sum - n - 2] = 1;
}
break;
}
}
//以上是找数字部分,下面是高精度乘法部分
int Anwser[500000] = { 0 };//这里空间够就用了int,否则是要用char的
Anwser[0] = Num[0];//初始化第一位
int Digit = 1;//答案数字的位数
int Last = 0;//进位指标
for (int i = 1; i < Cnt; i++)
{
for (int j = 0; j < Digit; j++)
{
int Temp = Anwser[j] * Num[i] + Last;
Anwser[j] = Temp % 10;
Last = Temp / 10;
}
while (Last)
{
Anwser[Digit++] = Last % 10;
Last /= 10;
}
}
int k = 0;
while (Num[k] == 1)
k++;//筛掉开始的1
cout << Num[k++];
for (; k < Cnt; k++)
if(Num[k]!=1)
cout << ' ' << Num[k];
cout << endl;
for (int l = Digit - 1; l >= 0; l--)
{
cout << Anwser[l];
}
}
return 0;
}