sincerit 算法竞赛宝典 乘积最大(经典动态规划)

本文介绍了一种使用动态规划解决最大乘积分割问题的方法,旨在寻找最佳方式将数字串分割成多个部分,使各部分乘积达到最大。通过具体实例和代码解析,深入探讨了算法的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。
同时,为了帮助正确理解题意,举如下的一个例子:
有一个数字串:312, 当N=3,K=1时会有以下两种分法:
312=36
31
2=62
这时,符合题目要求的结果是:312=62
再如 n = 310143
3
10143 = 0
310143 = 129
3
10143 = 126
310143 = 1290
3
10143 = 1260
310143 = 0
31
0143 = 372
31014*3 = 3720
最大乘积为3720

该题用动态规划做分析:
设有字符串为a1a2a3…an
当k=1时,最大值为max{a1a2a3…an, a1a2a3a4…an,a1a2a3*a4a5…an,…,a1a2a3…*an};
当k=2时,最大值为max{a1a2a3…a(n-2)*a(n-1)*an,a1a2a3…a(n-3)a(n-2)a(n-1)an,…,a1a2a3a4…an}
引入记号f(i,j,s)表示从i到j,s个乘号取得的最大值,g(i,j)表示从i到j的数字队列(字符变成整数)则:
k=1时,f(1,n,1)=max{g(1,1)*g(2,n), g(1,2)*g(3,n), …, g(1,n-1)*g(n,n)};
k=2时,f(1,n,2)=max{f(1, n-1, 1)*g(n,n), f(1, n-2, 1)*g(n-1, n), …,f(1, 2, 1)*g(3, n)};
由此推出
f(1,n,k) = max{f(1, n-1, k-1)*g(n,n), f(1, n-2, k-1)*g(n-1,n), …,f(1, k, k-1)*g(k+1, n)};
我们将f(1,j,s)化简成f(j, s)则转移方程为:
f(n,k) = max{f(n-1, k-1)*g(n,n), f(n-2,k-1)*g(n-1, n), …, f(k, k-1)*g(k+1,n)}

#include <stdio.h>
#include <cstring>
char str[50];
long long f[50][50]; 
// f[k][n]表示k个乘号,长度为n的最大乘积 
long long g(int start, int end) {
  long long s = 0, t = 1;
  for (int i = end; i >= start; i--) {
    s += (str[i]-'0') * t;
    t *= 10;
  }
  return s;
}
int main() {
  int n, k, mx;
  scanf("%d %d", &n, &k);
  scanf(" %s", str); // 下标从0开始  123456
  
  for (int i = 0; i < n; i++) f[0][i] = g(0,i); // 乘号为0个时的数字 
  for (int i = 1; i <= k; i++) {  
    for (int j = i; j < n; j++) { // 从i开始是因为i个乘号至少要i+1个字符,因为是从0开始的到i有i+1个字符 
      mx = f[i-1][j-1] * g(j,j); // j表示字符长度j+1以j结尾  i=1,j=4  f[0][3]*g(4,4)开始  12345 * 6
      for (int h = j-1; h >= i; h--) { // 乘号枚举的范围 i ~ j  因为j找过来所以 h = i ~ j-1 乘号枚举再h下标字符的后面  1234 * 56
        if (f[i-1][h-1] * g(h,j) > mx) {
          mx = f[i-1][h-1] * g(h,j); // 把字符串分解找到最大的那个乘积 也就是在0~h-1里有i-1个乘号再乘str[h~j] 
        }
      }
      f[i][j] = mx;
    }
  }
  printf("%lld\n", f[k][n-1]);
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值