题意 : 设 NUM 是一个 n 位十进制整数。如果将 NUM 划分为 k 段,则可得到 k 个整数。这 k 个整数的乘积称为 NUM 的一个 k 乘积。试设计一个算法,对于给定的 NUM 和 k,求出 NUM 的最大 k 乘积

 

分析 : 

定义 dp[i][j] = 前 i 个数字中间插入 j 个乘号时候的最大乘积是多少

初始化 dp[ i ][ 0 ] = NUM(1, i)  1 <= i <= len(NUM)

最后的结果则存于 dp[n][k] 

状态转移方程为 dp[i][j] = max( dp[i][j] ,  dp[m][j-1] * NUM[m+1 ~ i] )   j-1 < m < i

注 : NUM[a~b] 表示 NUM 的第 a 位到第 b 位组成的数字

 

关于状态转移方程先来看一个例子 n = 4、k = 2、NUM = 1231

首先初始化

dp[1][0] = 1

dp[2][0] = 12

dp[3][0] = 123

dp[4][0] = 1231

然后安插 1 个乘号的时候各个长度的最大乘积

dp[2][1] = dp[1][0] * 2 = 2

dp[3][1] = max( dp[1][0]*23、dp[2][0]*3 ) = 36

dp[4][1] = max( dp[1][0]*231、dp[2][0]*31、dp[3][0]*1 ) = 372

接着是安插 2 个乘号的时候

dp[3][2] = dp[2][1] * 3 = 6

dp[4][2] = max( dp[2][1]*31、dp[3][1]*1 ) = 62

 

细细去推一下这个例子,可能就会发现更加理解了这个 dp

可以看出这个 dp 定义的第二维应该是阶段、而第一维是 dp 的状态

换句话说只有知道在各个长度安插 1 个乘号的结果才能推出各个长度安插 2 个乘号的结果

 

C++版

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 15;
LL dp[maxn][maxn];
char num[maxn];

LL GetVal(int st, int en)
{
    LL ret = 0;
    for(int i=st; i<=en; i++)
        ret = ret * 10 + (num[i] - '0');
    return ret;
}

int main(void)
{
    int n, k;
    while(~scanf("%d %d", &n, &k)){
        scanf("%s", (num+1));
        for(int i=1; i<=n; i++)
            dp[i][0] = GetVal(1, i);

        for(int j=1; j<=k-1; j++)
            for(int i=j+1; i<=n; i++)
                for(int m=j; m<i; m++)
                    dp[i][j] = max(dp[i][j], dp[m][j-1]*GetVal(m+1, i));

        printf("%lld\n", dp[n][k-1]);
    }
    return 0;
}
View Code

相关文章:

  • 2022-01-01
  • 2022-02-07
  • 2022-12-23
  • 2021-08-16
  • 2022-02-23
  • 2022-12-23
  • 2021-07-26
  • 2021-11-03
猜你喜欢
  • 2021-06-09
  • 2022-02-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-01
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案