zoukankan      html  css  js  c++  java
  • 最大乘积

    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
  • 相关阅读:
    虚拟化基础介绍
    Linux---1.06.用户和用户组管理
    1.vmware tools的安装
    5.无法更新包,yum源更换无效
    Linux---1.05.软件包管理
    4.Vim的安装及待解决问题
    NSWorkspace的mountedLocalVolumePaths
    修改系统默认的NSButton的按下变灰
    NSParagraphStyleAttributeName
    - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
  • 原文地址:https://www.cnblogs.com/qswx/p/9308530.html
Copyright © 2011-2022 走看看