题解 P1249 【最大乘积】

· · 题解

【前言】

部分的题解是有一些瑕疵的。

1、贪心法求和sum,扣除sum-n,这种方法当n=8时,就错了。

这种方法的输出是2 3 5 ,但正确答案是3 5 。

2、动态规划通过ln函数,把求积改成求和,非常巧妙。背包是求不超过容积的最多装多少,但这道题是恰好装整个容积。这一点我没有想通。

3、这个题解的方法和已有的一个题解的思路是一致的。

【思路】

1、这道题预先需要知道的一点是:数越多,乘积越大。

“1”对于乘积是没有收益的,我们要从2~n-1中选出尽可能多的数,使它们的和为n。然后把选出的数相乘就是答案。

2、两个特例,“3”和“4”。这两个只能从拆成“1+2”和“1+3”,很多题解里的拆成的是“3”和“4”,哪种对,见仁见智吧。我单点测试了3和4,只能说洛谷的测试点中没有“3”和“4”,无论哪种输出都不会影响AC。

【流程】

输入——选数——高精度乘法——输出。(选数时和高精度乘法无关)

【如何选数】

我们就以13为例,“2”、“3”、“4”都可以选,但是“5”如果选了,sum(2,3,4,5)=14超过13,5不能选。此时sum(2,3,4)=9,还余4,我们把4这个值分配到“2”、“3”、“4”这三个数上。

怎么分最好?这里有两个原则:

(1)增长的数的个数越多,乘积越大。“2+1”、“3+1”、“4+2”优于“2”、“3”、“4+4”;

(2)若只有部分数能增长,那么本身大的增长后,乘积更大。“2”、“3”、“4+1”优于“2+1”、“3”、“4”。

所以对于 “2”、“3”、“4”,余4 来说。

可以都增长,那么变成 “2+1”、“3+1”、“4+1”,余1

不能都增长,那么变成 “3”、“4”、“5+1”,余0

vecotr<int>F; //存储可以选择的数,不熟悉vector,开个数组和一个记录数组长度的int len也可以。
void find(){
    int sum=0; //已选择的数之和
    for(int i=2;i<n;i++){ //遍历所有的数
        if(sum+i>n){ 
            int rm=n-sum; //余多少

            //把余的值分配给已选的数,从已选的数中,大数开始分配余值,若每个数增长1后,仍有余值,继续从大数开始分配
            while(rm){ 
                for(int j=F.size()-1;j>=0&&rm>0;j--){ 
                    F[j]++; //已选的数增长1
                    rm--; //余值减少1
                }
            }
            return ;
        }
        F.push_back(i); 
        sum+=i;
    }
}

【高精度乘法】

选数之后就是把所有选择的数相乘,因为最终值可能很大,所以需要用到高精度乘法,关于高精度乘法,不赘述。

【代码】

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int nA=10005 ;
int n;
vector<int>F; //存储可以选择的数

/*这一部分都是大整数模板代码。*/
struct Bign{
    int D[100000];
    int len;
    Bign(){
        fill(D,D+100000,0);
        len=0;
    }
};
Bign ans;
Bign change(string s){
    Bign a;
    a.len=s.size();
    for(int i=0;i<a.len;i++){
        a.D[i]=s[a.len-i-1]-'0';
    }
    return a;
}
Bign mult(Bign a,int b){
    Bign c;
    int carry=0;
    for(int i=0;i<a.len;i++){
        int tmp=a.D[i]*b+carry;
        c.D[c.len++]=tmp%10;
        carry=tmp/10;
    }
    while(carry!=0){
        c.D[c.len++]=carry%10;
        carry/=10;
    }
    return c;
}
void publish_Bign(Bign a){
    for(int i=a.len-1;i>=0;i--){
        cout<<a.D[i];
    }
}

/*输出*/  
void publish(){
    for(int i=0;i<F.size();i++){
        if(i)cout<<" ";
        cout<<F[i];
    }
    cout<<endl;
    publish_Bign(ans);
}

/*高精度乘法,计算所有可以选择的数的乘积*/           
void calc(){
    ans=change("1");
    for(int i=0;i<F.size();i++){
        ans=mult(ans,F[i]);
    }
}

/*选择和为n尽可能多的数*/  
void find(){
    int sum=0;
    for(int i=2;i<n;i++){
        if(sum+i>n){
            int rm=n-sum;
            while(rm){
                for(int j=F.size()-1;j>=0&&rm>0;j--){
                    F[j]++;
                    rm--;
                }
            }
            return ;
        }
        F.push_back(i);
        sum+=i;
    }
}

/*输入*/
void creat(){
    cin>>n;
}

int main(){
    creat(); //输入

    if(n==3){ //特判“3”
        cout<<"1 2"<<endl;
        cout<<"2";
    }
    else if(n==4){ //特判“4”
        cout<<"1 3"<<endl;
        cout<<"3";
    }
    else{ 
        find(); //选数
        calc(); //相乘
    }

    publish(); //输出
    return 0;
}