最大乘积

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 }
View Code
原文地址:https://www.cnblogs.com/qswx/p/9308530.html