清北学堂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; }