【例9.19】乘积最大

GitHub跳转原题关系图返回列表

把长度为 N 的数字串插入 K 个乘号切成 K+1 段使乘积最大。

OJ: roj

题目 ID: 1275

难度:普及-

标签:动态规划区间DP枚举python

创建: 2026-09-30 02:51

更新: 2026-10-05 07:45

题目描述

设有长度为 N 的数字串,用 K 个乘号切成 K+1 段(允许前导零),使各段整数乘积最大。第一行 N, K(6 ≤ N ≤ 10,1 ≤ K ≤ 6),第二行数字串;输出最大乘积。

样例:N=4, K=2,串 1231 → 输出 62。

思路

区间 DP:设 dp[i] 为前 i 位用当前刀数切出的最大乘积,按刀数逐层滚动更新,每层枚举最后一刀位置 t,转移 dp[i] = max(dp[t] * val(t, i)),答案为 dp[N]。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 02:51
# update_at: 2026-10-09 14:25

import sys


def solve() -> None:
    data = iter(sys.stdin.buffer.read().split())
    n = int(next(data))  # n 位数字
    k = int(next(data))  # 切 k 刀
    s = next(data).decode()  # 数字串本身是字符串,不能进 map(int),否则丢前导 0

    # dp[i]:前 i 位数字用掉当前刀数后能得到的最大乘积
    dp = [0] + [int(s[:i]) for i in range(1, n + 1)]  # 0 刀时整段就是一个因子

    for j in range(1, k + 1):  # 逐刀加入,第 j 层复用第 j-1 层的结果
        # 最后一刀落在第 t 位之后:左半段是上一层的 dp[t],右半段是子串 s[t:i]
        dp = [
            max((dp[t] * int(s[t:i]) for t in range(j, i)), default=0)
            for i in range(n + 1)
        ]

    print(dp[n])


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-04 14:30
 * update_at: 2026-10-05 07:45
 */
// main.cpp:把长度为 N 的数字串插入 K 个乘号切成 K+1 段,使各段整数乘积最大。
// DP:dp[i] 表示前 i 位用当前刀数能得到的最大乘积,逐层滚动枚举最后一刀位置 t。

#include <cstdio>
using namespace std;

typedef long long ll;

const int MAXN = 15;

int n, k;
char s[MAXN];
ll dp[MAXN]; // dp[i]:前 i 位用掉当前层刀数后的最大乘积

// 子串 [l, r) 的十进制值(l 从 0 起,半开区间),允许前导 0。
ll val(int l, int r) {
    ll v = 0;
    for (int i = l; i < r; i++) {
        v = v * 10 + (s[i] - '0');
    }
    return v;
}

void solve() {
    // 0 刀时整段就是一个因子
    for (int i = 1; i <= n; i++) {
        dp[i] = val(0, i);
    }
    dp[0] = 0;

    // 逐层加入第 j 把刀,第 j 层只依赖第 j-1 层的 dp
    for (int j = 1; j <= k; j++) {
        ll pre[MAXN];
        for (int i = 0; i <= n; i++) pre[i] = dp[i]; // 暂存上一层结果

        for (int i = 1; i <= n; i++) {
            ll best = 0;
            // 最后一刀落在第 t 位之后,左半段长 j 位是下限
            for (int t = j; t <= i - 1; t++) {
                ll cur = pre[t] * val(t, i);
                if (cur > best) best = cur;
            }
            dp[i] = best;
        }
    }

    printf("%lld\n", dp[n]);
}

int main() {
    // 输入:第一行 N K,第二行是长度为 N 的数字串
    scanf("%d %d", &n, &k);
    scanf("%s", s);
    solve();
    return 0;
}