洛谷-P1249 最大乘积

题目描述

一个正整数一般可以分为几个互不相同的自然数的和,如 3=1+23=1+2,4=1+34=1+3,5=1+4=2+35=1+4=2+3,6=1+5=2+46=1+5=2+4。

现在你的任务是将指定的正整数 nn 分解成若干个互不相同的自然数的和,且使这些自然数的乘积最大。

输入格式

只一个正整数 nn,(3 \leq n \leq 100003≤n≤10000)。

输出格式

第一行是分解方案,相邻的数之间用一个空格分开,并且按由小到大的顺序。

第二行是最大的乘积。

输入输出样例

输入 #1复制

10

输出 #1复制

2 3 5
30

分析:

这题博主不会,我大致能想到如果允许重复的话,那么对半分应该乘积是最大的,但是不允许重复,真的没有什么思路

看看题解吧

本题要先用简单的数论和贪心找到最优解的组成方法,再用高精度乘法求积。

以2004为例,由于把2004分拆成若干个互不相等的自然数的和的分法只有有限种,因而一定存在一种分法,使得这些自然数的乘积最大。

若1作因数,则显然乘积不会最大。把2004分拆成若干个互不相等的自然数的和,因数个数越多,乘积越大。为了使因数个数尽可能地多,我们把2004分成2+3…+n直到和大于等于2004。

若和比2004大1,则因数个数至少减少1个,为了使乘积最大,应去掉最小的2,并将最后一个数(最大)加上1。

若和比2004大k(k≠1),则去掉等于k的那个数,便可使乘积最大。

例如15:s=2+3+4+5+6刚好大于15,s-15=5,所以把5去掉。

又例如13:s=2+3+4+5刚好大于13,s-13=1,所以去掉2,并把5加1,即3 4 6。

高精度乘法我理解,最后可能会爆int,但是这个找的方法就很神奇,不知道为什么会这样想,往下翻翻,找到了其他题主的解释:

以下是本题的主要思路:
大家都清楚这题的意思,我们尽可能的把n分成更多份(都大于1)那样乘积最大。 (这里讲一下如果分出来的数可以重复,那么有时候并不是分出的份数越多越大,比如6,分成2,2,2不如分成3,3。但是不能重复的话就不存在这样的情况,大家可以想一下。) 以“6”为例,我们可以想到,我们先分出来一个2,然后再分出一个3......但是这样面临一个问题:“最后有余数怎么办”,比如“8”;分出来2,3还剩下3,没法再分出4,这时候怎么办呢?有的同学会想我把余数3都加到3身上,我们就分成了2和6,但是这样是最大的吗?(3和5才是最大的) 这时候我补充一下有一篇题解中所说:

“把余数分到大的数上比分到小的数上得到的乘积更大”。
实际不太准确,我们很容易证明出来如果能把数分到更小的数上,那么乘积更大(大家可以想想)。但是为什么最终是分到了大的数呢?就好比把6分成2,4,按照我们刚才的分法,先分出来了2和3最后余1,理论上我们把1给2得到的结果更大,但是我们不允许数重复,所以我们需要先把1给3,这样如果还剩下余数的话就分给2,所以当小的数被分配某个数后不会造成数的重复,那么优先给小的数分配,所以就像其他题解所说:

“从大数开始向前,依次分配1”。
所以对于8我们先分配出了2,3又余3,我们先分配1给3,得到2,4这时候2可以被分配,那么我们就分配给2一个1,得到3和4,这时候还余1,我们就分配给4,得到3,5。

这里我来解释一下点赞数最多的题解的思路。
以下为引用部分:
本题要先用简单的数论和贪心找到最优解的组成方法,再用高精度乘法求积。

以2004为例,由于把2004分拆成若干个互不相等的自然数的和的分法只有有限种,因而一定存在一种分法,使得这些自然数的乘积最大。

若1作因数,则显然乘积不会最大。把2004分拆成若干个互不相等的自然数的和,因数个数越多,乘积越大。为了使因数个数尽可能地多,我们把2004分成2+3…+n直到和大于等于2004。

若和比2004大1,则因数个数至少减少1个,为了使乘积最大,应去掉最小的2,并将最后一个数(最大)加上1。

若和比2004大k(k≠1),则去掉等于k的那个数,便可使乘积最大。

例如15:s=2+3+4+5+6刚好大于15,s-15=5,所以把5去掉。

又例如13:s=2+3+4+5刚好大于13,s-13=1,所以去掉2,并把5加1,即3 4 6。

大家可能觉得这个和我们刚才所讲的不太一样,为什么要减呢?怎么想到的呢?显然这样减,比我们一个个循环加更快。
我来帮大家推导一下:
我们分出来了2,3,4......n这n-1个数然后余数是K(1<=K<=n)。

如果K==n,我们需要进行两轮分配,意思是从n分配到2还剩下1,需要再回去把1分配给最大的数(也就是n+1)最终得到3,4,5......n+2这也就应对了上文题解中的情况2;
如果K<n,我们进行一轮分配就好因为我们的余数是K,那么分配到n+1-K就停止了,拿15举例,先分配出了2,3,4,5余1,我们分配到5就停止了,(n+1-K=5)。 对于上篇题解他先分配出2,3,4......n,n+1。因为我们分出来n-1个数余数是K,而分配出n+1后会造成数不够,还差n+1-K,那么这也就对应前两行我们推导出的结果,去掉n+1-K,就相当于分配到了n+1-K,应为n+1-K经过分配后变成了,n+2-K,这不就相当于去掉了吗。所以这相当于找规律了。

基本思想就是,对于一个数S,先用2+3+4+....+n的方法,找到和S临界的那个n,如果这一串数字刚好等于S,那么显而易见,答案就是2*3*4*...*n,但是情况并不总是这样

如果这一串数字的和比S大呢,假设大K,即余数为K,那么又该如何?

如果这个余数K==最后一个数字n,那么显而易见,给这一组数(n-1个数),每人分配一个1,就会变成3+4+5+...+n+(n+1),那么还多一个1,怎么分呢,总不能就补一个1上去吧,变成1+3+4+5+...+n+(n+1),这样分是可以,但是乘积并不是最大,为什么?因为1乘任何数都没有变化,反而白白浪费了1这个数。那加到最小的3上面?如果允许重复,那么加到3上面会让乘积最大(因为3最小,让最小的数变大,总乘积会最大化,很简单就可以验证),但是很遗憾不允许重复,那么就只有加到n+1身上了,变成n+2,即式子变成3+4+5+...+n+(n+2)

如果余数K<最后一个数字n呢,那就把2+3+4+....+n这组数从最后一个n开始,每个数加1,直到K分配完了为止,最后就会变成2+3+4+...+m+(m+2)+(m+3)+....+n+(n+1)+(n+2),请注意,中间断开了,没有m+1这一项,因为K分配到m+1之前,就已经用完了,这就是使乘积最大的序列了,有人会问,这和那个答主给的答案有什么关系呢?大家看我分析

按照答主的方式,直接找到2+3+4+5+....+n+(n+1)+(n+2),使得这一串数字大于S(注意是大于,前面分析的都是不大于),然后呢,大了K,那么怎么办,去掉其中值为K的那个数,而那个数在哪呢,如果你仔细分析,就会发现就是上一段中断开的m+1,即从这一串2+3+4+5+....+n+(n+1)+(n+2)挖去m+1,变成2+3+4+...+m+(m+2)+(m+3)+....+n+(n+1)+(n+2),和上面一模一样。

其他的不多说了,上代码吧

#include<iostream>
#include<string>
#include<vector>
using namespace std;
string add(string str1,string str2);
string mul(string str1,string str2);
string calc();
vector<int> list;
string ans;
int main()
{
	int n;
	cin>>n;
	int sum=0;
	
	for(int i=2;sum<n;i++)
	{
		list.push_back(i);
		sum+=i;
	}
	
	
	
	
	if(sum==n)//恰好多项式==n 
	{
		ans=calc();
	}
	else if(sum>n)//多项式>n 
	{
		int k=sum-n;
		
		if(k==1)//刚好差1,舍弃2,最大位+1 
		{
			list.erase(list.begin(),list.begin()+1);
			list[list.size()-1]+=1;
			ans=calc();
		}
		else//否则删除list中=k的那一项 
		{
			vector<int>::iterator it;
			int flag=0;
			for(it=list.begin();it<list.end();it++)
			{
				if(*it==k)
				{
					list.erase(it,it+1);
					flag=1;
				}
				if(flag)
				break;
			}
			
			ans=calc();
		}
		
	}
	
	cout<<ans;
	return 0;
}
string add(string str1,string str2)
{
	if(str1.length()<str2.length())
	{
		string s;
		s=str1;
		str1=str2;
		str2=s;
	}
	string str;
	int len1=str1.length();
	int len2=str2.length();
	int dis=len1-len2;
	int cf=0;
	for(int i=len2-1;i>=0;i--)
	{
		int temp=str1[i+dis]-'0'+str2[i]-'0'+cf;
		cf=temp/10;
		temp=temp%10;
		str=char(temp+'0')+str;
		//cout<<"cf is "<<cf<<" temp is "<<temp<<endl;
	}
	for(int i=dis-1;i>=0;i--)
	{
		int temp=str1[i]-'0'+cf;
		cf=temp/10;
		temp=temp%10;
		str=char(temp+'0')+str;
	}
	if(cf>0)
	str=char(cf+'0')+str;
	if(str.find_first_not_of('0')==str.npos)
	str="0";
	else
	str.erase(0,str.find_first_not_of('0'));
	return str;
}
string mul(string str1,string str2)
{
	if(str1.length()<str2.length())
	{
		string s;
		s=str1;
		str1=str2;
		str2=s;
	}
	string str="0";
	string tempstr;
	int len1=str1.length();
	int len2=str2.length();
	int dis=len1-len2;
	int cf=0;
	for(int i=len2-1;i>=0;i--)
	{
		tempstr.clear();
		cf=0;
		for(int j=len1-1;j>=0;j--)
		{
			int temp=(str1[j]-'0')*(str2[i]-'0')+cf;
			//cout<<"temp is "<<temp<<endl;
			cf=temp/10;
			//cout<<"cf is "<<cf<<endl;
			temp=temp%10;
			tempstr=char(temp+'0')+tempstr;
			//cout<<endl;
		}
		if(cf>0)
		tempstr=char(cf+'0')+tempstr;
		for(int k=1;k<=len2-1-i;k++)
			tempstr=tempstr+'0';
		
		if(tempstr.find_first_not_of('0')==tempstr.npos)
		tempstr="0";
		else
		tempstr.erase(0,tempstr.find_first_not_of('0'));
		//cout<<"tempstr is "<<tempstr<<endl;
		str=add(str,tempstr);
		//cout<<"str is "<<str<<endl;
	}
	if(str.find_first_not_of('0')==str.npos)
	str="0";
	else
	str.erase(0,str.find_first_not_of('0'));
	return str;
} 


string calc()
{
	int flag=0;
	vector<int>::iterator it1;
	for(it1=list.begin();it1<list.end();it1++)
	{
		if(flag==0)
		{
			cout<<*it1;
			flag=1;
		}
		else
		cout<<" "<<*it1;
	}
	
	cout<<endl;
			
	vector<int>::iterator it;
	it=list.begin();
	string s1;
	s1=char(*it+'0')+s1;
	//cout<<"s1: "<<s1<<endl;
	for(it=list.begin()+1;it<list.end();it++)
	{
		string s2;
		int temp=*it;
		while(temp)//当*it为两位,即12,13..时,无法通过 s2=char(*it+'0')+s2;一次获取s2了 
		{
			int tempyu=temp%10;
			s2=char(tempyu+'0')+s2;
			temp=temp/10;
		}
		
		//cout<<"s2: "<<s2<<endl;
		s1=mul(s1,s2);
		//cout<<"s2 is "<<s2<<"  s1: "<<s1<<endl;
	}
	return s1;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章