• 清北学堂2017NOIP冬令营入学测试P4749 C’s problem(c)


    P4746 C’s problem(c)
    时间: 1000ms / 空间: 655360KiB / Java类名: Main

    背景

    冬令营入学测试

    描述

    题目描述

             小C是一名数学家,由于它自制力比较差,经常通宵研究数学问题。

             这次它因为这个数学问题已经两天两夜没有睡觉了,再不研究出来就要出人命了!快帮帮它吧!

             这个问题是这样的,有一个数n,将其拆分成若干自然数之和,要求乘积最大!

             如果你以为问题仅仅这么简单,那你就太naive了。

             由于小C挑战自己的自我修养,它规定分成的自然数两两之间一定不能相等!

             它请你输出这个乘积最大是多少,但这个答案太大了,小C并没有兴趣看那么长的数字,它只想知道这个数对1000000007取模后的值是多少。

    输入格式

    一行一个数表示n

    输出格式

    一个数表示答案

    备注

    输入样例

    6

    输出样例

    8

    数据范围

    对于30%的数据n<=10。

    对于50%的数据n<=10000。

    对于100%的数据1<=n<=1000000000。

    30分暴力

    #include<iostream>
    #include<cstdio>
    #define mod 1000000007
    using namespace std;
    int n;
    long long ans;
    bool v[100001];
    void dfs(int now,int remain,long long s)//now当前分出来的数,remain还剩下多少,s乘积 
    {
        if(remain==0)
        {
            ans=max(s,ans);
            return ;
        }
        for(int i=now+1;i<=remain;i++)
        {
            if(!v[i])//不能有重复 
            {
                v[i]=true;
                dfs(i,remain-i,s*i%mod);
                v[i]=false;
            }
        }
    }
    int main()
    {
        scanf("%d",&n);
        if(n==1)//1不用拆 
        {
            cout<<1;
            return 0; 
        }    
        dfs(0,n,1);
        if(ans==n) cout<<ans-1;//2、3、4的结果应该是1、2、3,但dfs结果是2,3,4,因为拆分成了0和本身。dfs时从1开始,所以2,3,4的0乘本身算成了本身。>4的数答案大于本身,所以不用管 
        else cout<<ans;
    }

    AC做法:

    本题不难,难就难在找规律。
    我们设待拆分数为n
    看:
    5=2*3
    6=2*4  4是3+1得到的
    7=3*4  3是2+1得到的
    8=3*5  5是4+1得到的
    9=2*3*4  数的个数增加1个,第1个数变成从2开始
    10=2*3*5   5是4+1得到的
    11=2*4*5 4是3+1得到的
    12=3*4*5  3是2+1得到的
    13=3*4*6  6是5+1得到的
    14=2*3*4*5  数的个数增加1个,第1个数变成从2开始
    由此得到第一个规律,对于数n,要么是n-1中的某一个数+1,要么是数的个数+1,第1个数变成2,往后递增
    那么,什么时候对前一个数+1,什么时候数的个数+1呢?
    我们再看:
    本题要求乘积最大,设2个数a,b,且a<b,那么(a+1)*b=a*b+a,a*(b+1)=a*b+b;所以(a+1)*b<a*(b+1)。
    由此可以得出结论,若给拆分后的数+1,那么最优解应该是给大的数+1
    继续观察规律:
    9拆成2*3*4,3个数
    10:4(第3个数)+1
    11:3(第2个数)+1
    12:2(第1个数)+1
    13:5(第3个数)+1
    所以第二个规律:对于数n-1得到n时,给某个数+1是倒着来加的(语言表达能力有限,不懂请留言)
    接下来,什么时候是数的个数+1呢?
    5是2个数的开始,拆成2*3
    9是3个数的开始,拆成2*3*4
    14是4个数的开始,拆成2*3*4*5
    由此可以推出
    20是5个数的开始,拆成2*3*4*5*6
    所以第三个规律:对于数n,若是可以拆成2*3*4*5*6……的形式,那个到n的最优解就是数的个数+1,第1个数变成2,往后递增
    为什么我是从5开始找规律呢?因为当n<=4时,在拆出的数不相等的条件下,其本身最大,可以自己验证。
    所以第四个规律:当n<=4时,答案就是本身
    由以上4个规律,得到最终算法:
    0、if(n<=4) cout<<n,结束
    1、把数n从2开始分,拆成2*3*4*5……,直至拆到不够拆为止
    2、如果n恰好拆完,那么答案就是2*3*4*5*6……,结束程序
    3、    如果n拆不完,设n拆出的数分别为a(1)、a(2)、a(3)、a(4)……a(m)。
             将剩下的数从a(m)开始,依次给a(m)加1、a(m-1)加1、a(m-2)……a(1)加1,直至加到n剩下的数全都加进去。若加到a(1),n剩下的数还没加完,      则再从a(m)开始。
    4、最终答案就是a(1)*a(2)*a(3)*……a(m)
    #include<cstdio>
    #define mod 1000000007
    using namespace std;
    int n,now=2,a[1000000],cnt;
    long long ans=1;
    int main()
    {
        scanf("%d",&n);
        if(n<=4)
        {
            printf("%d",n);
            return 0;
        }
        while(n>=now)
        {
            a[++cnt]=now;
            n-=now;
            now++;
        }
        if(!n)
        {
            for(int i=1;i<=cnt;i++)
             ans=a[i]%mod*ans%mod;
            printf("%d",ans);
            return 0;
        }
        int tot=cnt;
        while(n)
        {
            if(!tot) tot=cnt;
            a[tot--]++;
            n--;
        }
        for(int i=1;i<=cnt;i++)
             ans=a[i]%mod*ans%mod;
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    Ubuntu16.04 + OpenCV源码 + Qt5.10 安装、配置
    DML和DQL
    初识MySql
    表单校验
    使用jQuery操作DOM
    jQuery中的事件与动画
    jQuery选择器
    初识jQuery
    JavaScript对象及初识OOP
    JavaScript操作DOM对象
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6212896.html
Copyright © 2020-2023  润新知