将一个整数n分解成x个正整数(a1+a2+a3+...+ax=n),使它们的乘积(a1*a2*a3*...*ax)最大。
答案可能很大,输出对m取模后的结果。
答案可能很大,输出对m取模后的结果。
思路一:动态规划(DP)
可以很容的思考出来动态转移方程
用dp[i]代表当n=i的时候的最大乘积,于是:
DP[i]=max(DP[i-j]*DP[j])
动态规划的方法虽然可以解决该类问题,但是当n的值非常大的时候,dp数组都很难开的下,故不管是时间复杂度还是空间复杂度都高得惊人!
于是需要新的思路。
思路二:
按照我们数学直觉,应该不难想到,分出来的这些a1,a2,a3…应该要相等才能让他们的积最大。
/*小学的时候大家应该学过同样的周长,要让面积最大,则围成正方形是最好的选择(除了圆)*/
既然a1,a2,a3…都相等,那么可以假定x=a[i]
于是乘积f(x)=x^(n/x)(x为正整数)
现在问题转化为当x为何值的时候让f(x)最大
我们可以假定g(x)=ln(f(x))=(n/x)*lnx
很明显,当x让g(x)取最大的时候,f(x)也得到了最大值。于是我们可以对g(x)求一次导数
g’(x)=(n-n*lnx)/(x^2)=[n*(1-lnx)]/(x^2)
故我们可以看到,当x=e(2.71828……)时g(x)取得极大值。但是因为x为正整数,所以只有分别比较x=2以及x=3两种情况下的f(x)谁更大
既然现在已经知道把一个数分为几个数的和,且让他们的积最大,就是要把他们尽可能多的分为2或者3,问题就进一步简化了。
我们只需要举几个例子便可以证明x到底取2还是x取3使得乘积最大。
6=2+2+2=3+3
2*2*2=8
3*3=9
因为f(x)是对所有的n而言的,故如果一个例子成立就代表所有情况都成立!
至此,最大乘积的问题就快结束了。但是还有一个问题,虽然知道了尽可能让n多分出几个3出来,但是总会有余数吧,余数怎么处理呢?
当考虑到当n=4的时候,如果分出3,就必然会得到一个1,显然不是最大的乘积(不是不符合刚刚的公式的推导,因为此时只有一个3,不能叫分成了几个相同的数)所以可以分情况考虑下
在此,我们就不在做更多的证明了,乘积最大问题的一般处理如下:
当n<=4的时候ans=n%m;
当n>4且n%3==0的时候
n全部分为3
当n>4且n%3==1的时候
把(n-4)的部分全部分为3,剩下的4直接乘上去
当n>4且n%3==2的时候
把(n-2)的部分全部分为3,剩下的2直接乘上去
可以使用二分幂对m取余,把时间复杂度降为O(logn)的
#include<iostream> using namespace std; int solve(int n,int m) { int i,s=1; if(n<=4)return n%4; else { if(n%3==0) { for(i=1;i<=n/3;i++) s=s*3%m; return s; } else if(n%3==1) { for(i=1;i<=(n-4)/3;i++) s=s*3%m; return s*4%m; } else { for(i=1;i<=(n-2)/3;i++) s=s*3%m; return s*2%m; } } } int main() { int n,m; freopen("d:\\1.txt","r",stdin); while(scanf("%d%d",&n,&m)!=EOF&&(n||m)) { cout<<solve(n,m)<<endl; } return 0; }