Luogu - P1018 乘积最大 - 题解

原文:https://www.luogu.org/problemnew/solution/P1018?page=7

题目:P1018【乘积最大】



前言:

  • 这题的正解理论上说是DP,可是由于民间数据太水,用暴力过并不难

整体思路:

  1. 利用一个b数组标记每一位之间是否分割(1位分割,0为连接)。
  2. 利用STL里的 next_permutation 求出b的各种排列(即暴力枚举每种情况)。
  3. 由于本题数据规模大,所以要使用高精度计算每种分割的最后结果,并找出最大。

next_permutation函数:

  • 即STL里的求全排列函数,所求的数组必须是升序,否则将无法求出全部的排列方式(这和它生成群排列的方式有关),next_permutation正常和sort一样,有2个参数,分别是数组的首地址和尾地址,并返回一个bool量,即能否求出下一个全排列,可以的话返回true,并将指定数组变为下一个排列方式,如1 2 3的下一个排列方式就是 1 3 2。


上代码:

#include<algorithm> //使用next_permutation需要调用的头文件
#include<cstdio>  //c语言读入输出
#include<cstring>  //处理高精度字符串时需要用到

using namespace std;

struct BigN{  //高精度(即大整数)运算
    int num[1001]={0},len;
    BigN(char s[])  //构造函数,用于给新定义的大整数赋值
    {
        len=strlen(s);
        for(int i=len-1;i>=0;i--)
            num[i]=s[len-i-1]-‘0‘;
    }
    void clean()  //用于清零
    {
        memset(num,0,sizeof(num));
    }
    void f(int n)  //将一个普通整数压到大整数的开头,这个在后面分割每一位时会用到
    {
        for(int i=len;i>0;i--)
            num[i]=num[i-1];
        len++;
        num[0]=n;
    }
    void cheng(BigN n)//高精度乘法,这里就不过多解释了,有疑问可以前往 P1303 了解更多
    {
        BigN c("0");
        int s=0,g=0;
        for(int i=0;i<=len;i++)
            for(int j=0;j<=n.len;j++)
            {
                int w=i+j;
                s=num[i]*n.num[j];
                c.num[w]+=s%10;
                c.num[w+1]+=s/10+c.num[w]/10;
                c.num[w]%=10;
            }
        c.len=len+n.len;
        while(c.num[c.len]==0&&c.len>=0)c.len--;
        fz(c);
    }
    void fz(BigN n) //将一个大整数赋值给例外一个大整数,相当于‘=‘
    {
        len=n.len;
        for(int i=0;i<=n.len;i++)
            num[i]=n.num[i];
    }
    bool bj(BigN n)  //判断两个大整数的大小,用于找出最大结果
    {
        if(len>n.len)
            return 1;
        else if(len<n.len)
            return 0;
        else
        {
            for(int i=len;i>=0;i--)
                if(num[i]<n.num[i])
                    return 0;
                else if(num[i]>n.num[i])
                    return 1;
            return -1;
        }
    }
    void out() //输出
    {
        for(int i=len;i>=0;i--)
            printf("%d",num[i]);
    }
};

int n,k,sum[55],b[55],i,j; //常规定义,不多做解释
BigN mmax("0");

int main()
{
    char s[101];  //s用于读入一个大整数
    scanf("%d%d%s",&n,&k,&s);
    for(i=0;i<strlen(s);i++)  //在sum中备份一份原数
        sum[i]=s[i]-‘0‘;
    for(i=n-2;i>=(n-k)-1;i--)  //将b数组中的后k个数赋1,因为使用next_permutation需要让数组升序,否则可能无法找出所有排列方式
        b[i]=1;
    do{
        BigN temp("0"),all("1");//temp用于存放分割后的每一节,all用于计算每种排列方式的结果
        i=0;
        while(i<n)//分割
        {
            if(i!=0)
                if(b[i-1]==1)//如果b[i-1]为1,那么就要在这一位加上一个乘号,即将原数分割
                    all.cheng(temp),temp.clean();//总数乘上分割后的每一位,并将temp清空,用于储存下一节.
            temp.f(sum[i]),i++; //将原数的下一位压到temp的最前面
        }
        all.cheng(temp);//由于temp还没有乘all就退出循环,所以要再乘一次
        if(mmax.bj(all)==0)//如果这种排列顺序的结果大于之前最大的结果,刷新最大结果
            mmax.fz(all);
    }while(next_permutation(b,b+n-1));//调用next_permutation
    mmax.out();//输出
    return 0;
}

原文地址:https://www.cnblogs.com/hao-qing233/p/11828874.html

时间: 2024-10-09 19:53:11

Luogu - P1018 乘积最大 - 题解的相关文章

luogu P1541 乌龟棋 题解

\(luogu\) P1541 乌龟棋 题解 题目描述 这道题目想状态的时候想多了一维表示当前走了多少步,其实这个完全没有必要,因为根据你的牌的使用就可以知道你当前在哪一个位置. 状态 设\(f[i][j][k][l]\)表示已经用了\(i\)张步数为\(1\)的牌,\(j\)张步数为\(2\)的牌,\(k\)张步数为\(3\)的牌,\(l\)张步数为\(4\)的牌所能取得的最大分数. 转移 因为只有这四种牌,所以只需要枚举到达当前的步数的最后一步用的那张牌,然后对用四种牌的情况取\(\max\

P1018 乘积最大

P1018 乘积最大 题目描述 今年是国际数学联盟确定的“20002000――世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰9090周年.在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZXZ也有幸得以参加.活动中,主持人给所有参加活动的选手出了这样一道题目: 设有一个长度为NN的数字串,要求选手使用KK个乘号将它分成K+1K+1个部分,找出一种分法,使得这K+1K+1个部分的乘积能够为最大. 同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:

洛谷P1018乘积最大——区间DP

题目:https://www.luogu.org/problemnew/show/P1018 区间DP+高精,注意初始化和转移的细节. 代码如下: #include<iostream> #include<cstdio> #include<cstring> #define MAXN 20005 using namespace std; typedef long long ll; ll n,k,a[45],f[45][7][MAXN],tmp[MAXN],num[MAXN]

[luogu]P1262 间谍网络 题解

原题目:[luogu]P1262 间谍网络 数据范围好小啊(小声) 首先对于环 , 我们可以直接缩成点 , 如果环上的有好多可以收买的间谍的话就找其中要价最低的作为这个缩点后的点的要价 然后怎么处理呐? 我做完以后看到题解区有神仙一个循环就能处理出答案 但是我太菜了自己做的时候并没有想到qaq 所以就用了 DFS 再建一个缩点后的图 , 然后从每一个可以被收买的点上 DFS , 最后看看有没有点没有被搜到就可以了 如果说从一个可以被收买的点 a , 搜的时候找到了另一个已经搜过而且也是可以被收买

洛谷P1018 乘积最大 区间动归

题目描述 今年是国际数学联盟确定的"2000――世界数学年",又恰逢我国著名数学家华罗庚先生诞辰90周年.在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加.活动中,主持人给所有参加活动的选手出了这样一道题目: 设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大. 同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子: 有一个数字串:312, 当N=3,K=1时

[NOIP2000] 提高组 洛谷P1018 乘积最大

题目描述 今年是国际数学联盟确定的“2000――世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年.在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加.活动中,主持人给所有参加活动的选手出了这样一道题目: 设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大. 同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子: 有一个数字串:312, 当N=3,K=1时会有以下两

【日常学习】【划分DP】codevs1017 乘积最大题解

题目来源 2000NOIP 题目描述 Description 今年是国际数学联盟确定的"2000--世界数学年",又恰逢我国著名数学家华罗庚先生诞辰90周年.在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加.活动中,主持人给所有参加活动的选手出了这样一道题目: 设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大. 同时,为了帮助选手能够正确理解题意,主持人还举了如下的一

[洛谷] P1018 乘积最大

题目描述 今年是国际数学联盟确定的“2000――世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年.在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加.活动中,主持人给所有参加活动的选手出了这样一道题目: 设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大. 同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子: 有一个数字串:312, 当N=3,K=1时会有以下两

【luogu P3952 时间复杂度】题解

对于2017 D1 T2 这道题 实实在在是个码力题,非常考验耐心. 其实大体的思路并不是非常难想出来,但是要注意的小细节比较多. 题目链接:https://www.luogu.org/problemnew/show/P3952 思路 对于每一个程序,先读入L和O(),并将其中的时间复杂度抠出来. 其次整行读入字符串,即所给定的程序. 判断第一个字符是F or E F i x y 需要把x y拿出来,把i压进栈 E 退栈 压进i后为了方便退栈及退栈时判断,用一个flag标记 每做完一个程序,与前