问题引入
设 I 是一个 n 位十进制整数。如果将 I 划分为 k 段,则可得到 k 个整数。这 k 个整数的乘积称为 I 的一个 k 乘积。设计一个算法,对于给定的 I 和 k ,求出 I 的最大 k乘积 。
例如十进制整数 12345 划分为 3 段可有如下情形:
算法分析
代码实现
支持负数和打印段
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int i, j, d, n, k, ans;
int dp[100][100], num[100][100];
int path[100][100];
string str; // 在考虑负数的条件下,字符串比字符数组更好用
void show(int n, int k)
{
if (k > 0)
{
show(path[n][k], k - 1);
printf("[%d...%d]", path[n][k] + 1, n);
}
}
int fun()
{
for (i = 1; i <= n; i++)
{
for (j = i; j <= n; j++)
{
num[i][j] = num[i][j - 1] * 10 + str[j - 1] - '0';
}
dp[i][1] = num[1][i]; // 解决了仅分为一段的情况 (因为当总长度为1时只能分为一段,所以还解决了总长度为1时的所有分段可能)
}
for (i = 2; i <= n; i++) // 开始解决总长度大于等于2的情况
{
for (j = 2; j <= k; j++) // 开始解决总长度大于等于2,且段数也大于等于2的情况
{
dp[i][j] = -1;
for (d = 1; d < i; d++)
{ // 不断调整当前段前面几部分,再与末尾段相乘
int temp = dp[d][j - 1] * num[d + 1][i]; /// dp[i][j]代表长度为i段数为j的最大乘积;num[i][j]代表第i位数到第j位数组成的数字
if (dp[i][j] < temp)
{
dp[i][j] = temp;
path[i][j] = d;
}
}
}
}
return dp[n][k];
}
int main()
{
cin >> n >> k;
cin >> str;
if (str[0] == '-') /// 负数情况
{
ans = 1;
str.erase(0, 1); // 字符串删掉最开始的符号,后面的字符依次前移
for (i = 0; i < n; i++)
ans *= (str[i] - '0'); // 为负数时,每一位都作为一段,就可获得最大乘积值
cout << -1 * ans << endl;
show(n, k); // 输出具体的分段方案
return 0;
}
else /// 正数情况
{
ans = fun();
cout << ans << endl;
show(n, k); // 输出具体的分段方案
return 0;
}
}
/*
4 2
1231
5 2
12345
输出:6170
*/
不支持负数
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int i, j, d, n, k, ans;
int dp[100][100], num[100][100];
char c[100];
int path[100][100];
void show(int n, int k)
{
if (k > 0)
{
show(path[n][k], k - 1);
printf("[%d...%d]", path[n][k] + 1, n);
}
}
int main()
{
cin >> n >> k;
for (i = 1; i <= n; i++)
cin >> c[i];
for (i = 1; i <= n; i++)
{
for (j = i; j <= n; j++)
{
num[i][j] = num[i][j - 1] * 10 + (c[j] - '0');
}
dp[i][1] = num[1][i]; // 解决了任意长度下仅分为一段的情况 (因为当总长度为1时只能分为一段,所以还解决了总长度为1时的所有分段可能)
}
for (i = 2; i <= n; i++) // 开始解决总长度大于等于2的情况
{
for (j = 2; j <= k; j++) // 开始解决总长度大于等于2,且段数也大于等于2的情况
{
dp[i][j] = -1;
for (d = 1; d < i; d++)
{ // 不断调整当前段前面几部分,再与末尾段相乘
int temp = dp[d][j - 1] * num[d + 1][i]; /// dp[i][j]代表长度为i段数为j的最大乘积;num[i][j]代表第i位数到第j位数组成的数字
if (dp[i][j] < temp)
{
dp[i][j] = temp;
path[i][j] = d;
}
}
}
}
cout << dp[n][k] << endl;
show(n, k); // 输出具体的分段方案
}
/*
4 2
1231
5 2
12345
输出:6170
*/