Problem description
一个正整数一般可以分为几个互不相同的自然数的和,如3=1+2,4=1+3,5=1+4=2+3,6=1+5=2+4,…。
现在你的任务是将指定的正整数n分解成若干个互不相同的自然数的和,且使这些自然数的乘积最大。
Input format
只一个正整数n,(3≤n≤10000)。
Output format
第一行是分解方案,相邻的数之间用一个空格分开,并且按由小到大的顺序。
第二行是最大的乘积。
Algorithm design
暴力找规律+模拟处理
Problem analysis
在不考虑“互不相同”这个条件下,此题非常简单,利用正方形定则
将一个数不断分成相差无几的两份,直到出现比5小的数字(从4到1拆分无益)
最后将拆分出的数字输出即可。
加上“互不相同”之后通过数学证明,将问题简化如下:
对于递增数列,总和增加1,使乘积最大且无重复。
有两种处理方式:
1.将某一位数+1。设原乘积为x,数字i+1,结果为x*(i+1)/i,当i最小时,效益最大,又为防止重复,在等差为1环境下只能从最高位加到最低位
2.增加一个新数字。设原乘积为x,
若增加数字1,结果不变,为x,无意义;
若增加数字2,取走数字i,结果为x*(i-1)*2/i,当i最大时,(i-1)/i影响最小
若增加数字3,取走数字i,结果为x*(i-2)*3/i
3的效益明显大于2,然而在1处理的影响下,3一定已在数列中,又因为1不会在队列中,理由同上,1处理后不会出现2,所以最高位-1,增加数字2
综上,因操作2有限制条件,在规律3发生后一段时间内(时间长度为数列长度)无法发生,此时采取操作1,直到2被修改为3后,数列为等差1数列,无法将最高位-1,继续操作1修改最高位,此时操作2激活。因为操作1和操作2的效益比为:(i+1):(i-1)*2,当i足够大时,操作2效益几乎是操作1效益2倍,故直接采取操作2,完全契合以上规律。
证毕。
找到规律之后为了更好的使用,可以放在数学环境下,利用上述规律,操作2的冷却时长或者说数列保持某一长度的时间-2其实就是数列的长度,此数值递增
省略计算过程,得出操作2发生的时间为(i^2+7*i+10)/2,至于其他规律一切用模拟执行
要用高精度
Source code

1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 struct bign 6 { 7 int len,num[40010]; 8 void clean() 9 { 10 while(len>1&&!num[len-1]) 11 len--; 12 } 13 bign operator =(string st) 14 { 15 memset(num,0,sizeof(num)); 16 len=st.size(); 17 for(int i=0;i<len;i++) 18 num[i]=st[len-i-1]-'0'; 19 return *this; 20 } 21 bign operator =(bign n) 22 { 23 memset(num,0,sizeof(num)); 24 len=n.len; 25 for(int i=0;i<len;i++) 26 num[i]=n.num[i]; 27 return *this; 28 } 29 bign operator +(bign n) 30 { 31 bign ans; 32 memset(ans.num,0,sizeof(ans.num)); 33 ans.len=max(len,n.len); 34 int accu=0; 35 for(int i=0;i<ans.len;i++) 36 { 37 int res=num[i]+n.num[i]+accu; 38 ans.num[i]=res%10; 39 accu=res/10; 40 } 41 if(accu)ans.num[ans.len++]=accu; 42 return ans; 43 } 44 bign operator +=(bign n) 45 { 46 *this=*this+n; 47 return *this; 48 } 49 bign operator *(int n) 50 { 51 bign ans; 52 memset(ans.num,0,sizeof(ans.num)); 53 ans.len=len; 54 for(int i=0;i<ans.len;i++) 55 ans.num[i]=num[i]*n; 56 int accu=0; 57 for(int i=0;i<ans.len;i++) 58 { 59 ans.num[i]+=accu; 60 accu=ans.num[i]/10; 61 ans.num[i]%=10; 62 } 63 while(accu)ans.num[ans.len++]=accu%10,accu/=10; 64 ans.clean(); 65 return ans; 66 } 67 bign operator *=(int n) 68 { 69 *this=*this*n; 70 clean(); 71 return *this; 72 } 73 friend ostream& operator <<(ostream& out ,bign n) 74 { 75 for(int i=n.len-1;i>=0;i--) 76 out<<n.num[i]; 77 return out; 78 } 79 }; 80 81 int n,sub,mx; 82 bool mark[20001],vit[20001]; 83 84 int main() 85 { 86 freopen("test.in","r",stdin); 87 freopen("test.out","w",stdout); 88 for(int i=0;i<=138;i++) 89 mark[(i*i+7*i+10)/2]=true; 90 //138刚好卡到10000,(i^2+7*i+10)/2为第i长度增长的时间点 91 cin>>n; 92 if(n<=4) 93 { 94 cout<<n<<endl<<n<<endl; 95 return 0; 96 } 97 mx=sub=4,vit[4]=true; 98 for(int i=5;i<=n;i++) 99 { 100 if(mark[i]) 101 vit[mx--]=false,vit[mx]=true,vit[2]=true,sub=mx; 102 else 103 { 104 vit[sub++]=false,vit[sub]=true; 105 if(sub>mx)mx=sub; 106 sub-=2; 107 if(sub==1)sub=mx; 108 } 109 } 110 bign ans; 111 ans="1"; 112 for(int i=1;i<=n;i++) 113 if(vit[i])cout<<i<<" ",ans*=i; 114 cout<<endl<<ans<<endl; 115 return 0; 116 }