【8月刷题打卡】乘积最大(动态规划 | 高精度)

113 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

[NOIP2000 提高组] 乘积最大

题目描述

今年是国际数学联盟确定的“ 2000 ――世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰 90 周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友 XZ 也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:

设有一个长度为 NN 的数字串,要求选手使用 KK 个乘号将它分成 K+1K+1 个部分,找出一种分法,使得这 K+1K+1 个部分的乘积能够为最大。

同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:

有一个数字串:312312, 当 N=3,K=1N=3,K=1 时会有以下两种分法:

  1. 3×12=363 \times 12=36

  2. 31×2=6231 \times 2=62

这时,符合题目要求的结果是: 31×2=6231 \times 2 = 62

现在,请你帮助你的好朋友 XZ 设计一个程序,求得正确的答案。

输入格式

程序的输入共有两行:

第一行共有 22 个自然数 N,KN,K6N40,1K66≤N≤40,1≤K≤6

第二行是一个长度为 NN 的数字串。

输出格式

结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。

样例 #1

样例输入 #1

4  2
1231

样例输出 #1

62

提示

NOIp2000提高组第二题

dp[k][i]表示前i个数,加入k个乘号时的最大值

我们需要枚举最后一个乘号是在哪里放的

转移时,直接计算一下最后一个乘号之前的数字加入k-1个乘号时的最大值,再乘以最后一个乘号之后的数字

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=100;
int n,K;
string s;
int a[maxn];
struct lxt
{
    int len;
    int ans[maxn];
}dp[maxn/10][maxn];

lxt cal(lxt x,int l,int r)
{
    lxt Ans,y;
    memset(Ans.ans,0,sizeof(Ans.ans));
    memset(y.ans,0,sizeof(y.ans));
    y.len=r-l+1;
    for(int i=r;i>=l;--i) y.ans[r-i+1]=a[i];
    int l1=x.len,l2=y.len,ll;
    for(int i=1;i<=l1;++i)
      for(int j=1;j<=l2;++j)
          Ans.ans[i+j-1]+=x.ans[i]*y.ans[j];
    ll=l1+l2-1;      
    for(int i=1;i<=ll;++i)
    {
        Ans.ans[i+1]+=Ans.ans[i]/10;
        Ans.ans[i]=Ans.ans[i]%10;
    }
    if(Ans.ans[ll+1]) ll++;
    Ans.len=ll;
    return Ans;
}
lxt cmp(lxt x,lxt y)
{
    int lx=x.len,ly=y.len;
    if(lx<ly) return y;
    if(lx>ly) return x;
    for(int i=lx;i>=1;--i)
    {
        if(x.ans[i]>y.ans[i]) return x;
        if(x.ans[i]<y.ans[i]) return y;
    }
    return x; 
}
int main()
{
    scanf("%d%d",&n,&K);
    cin>>s;
    for(int i=1;i<=n;++i) a[i]=s[i-1]-'0';
    for(int i=1;i<=n;++i)
      for(int j=i;j>=1;--j)
        dp[0][i].ans[++dp[0][i].len]=a[j];
    for(int i=2;i<=n;++i)
      for(int k=1;k<=min(K,i-1);++k)
        for(int j=k;j<i;++j)
          dp[k][i]=cmp(dp[k][i],cal(dp[k-1][j],j+1,i));
    for(int i=dp[K][n].len;i>=1;--i)
      printf("%d",dp[K][n].ans[i]);
    return 0;
}