原题链接
算法分析
要想 AC 此题,就先要用简单的数论和贪心找到最优解的组成方法。
以 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 。
示例代码
#include <iostream>
using namespace std;
int n , sum , ans[10001] , c = 1;//ans数组用来存拆分出来的数
int main()
{
ios::sync_with_stdio(0);
cin >> n;
if ( n <= 4 )//特判,如果n小于等于4,自己本身就是最优解
{
cout << n;
return 0;
}
for ( int i = 2 ; i <= n ; i++ )//2到n循环
{
if ( n >= i ){
n -= i;//每拆分出1个数,n就减去这个数
ans[c++] = i;//把i存下来
}
else
break;//不能再拆分就终止循环
}
for ( int i = c - 1 ; i >= 1 ; i-- )//逆序倒推
if ( n > 0 ) ans[i]++ , n--;//多的数分担给其他数
if ( n > 0 ) ans[c-1]++;//如果还多,就分给最后一个数
for ( int i = 1 ; i < c ; i++ )
cout << ans[i] << " ";//输出每一个拆分出来的数
return 0;
}