先看题目:
在一个由n个数字组成的数字串中插入r个乘号(1 <= r < n),将它分成r+1个整数,找出一种乘号的插入方法,使得这r+1个整数的
乘积最大。
例如,对给定的数串267315682902764如何插入6个乘号,使其乘积最大?
插入r个乘号是一个多阶段决策问题,应用动态规划来求解。
使用动态规划需要找到状态递推关系,阶段自然就是插入的乘号了。
设 f(i, k)表示在前i位数中插入k个乘号所得乘积的最大值,a(i, j)表示从第i位到第j位组成的整数值,先看一个实例:
对给定的的9个数字的数串84731926,如何插入5个乘号,使其乘积最大?
目标是f(9, 5)。
设前8个数字中已插入4个乘号,则最大乘积为f(8,4)* 6
设前7个数字中已插入4个乘号,则最大乘积为f(7,4)* 26
设前6个数字中已插入4个乘号,则最大乘积为f(6,4)* 926
设前5个数字中已插入4个乘号,则最大乘积为f(5,4)* 1926
一般为了求取f(i,k),考察数字串的前i个数字,设在前j (k<= j < i)个数字中已插入k-1个乘号的基础上,在第j个数字后插入第k个乘号,此时的最大乘积为f(j, k-1)* a(j+1,i)。
由此可得递推关系式:
f(i, k) = Max( f(j, k - 1) * a(j+1, i) ));
边界就是当k=0的时候,即插入0个乘号。在递推的过程中,可以使用额外的c[i][j]数组来记录乘号插入的位置。
这道题难度中等,思路不容易想到,此外就是编码的时候容易出错,需要多多练习。
下面是本题的c代码实现:
/*
* n个数插入r个乘号使得乘积最大
* f[i][j] 表示前i个数中插入j个乘号的最大值。
* f[i][j] = MAX(f[i-k][j-1]*a[i-k+1 ~ i])
*/
#include <stdio.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))
void main()
{
double f[30][20] = {0}, d;
int a[30] = {0}, i, j, k, m, n, r, c[30][20] = {0}, b[20];
char l[30];
printf("输入数列:"); scanf("%s",l);
printf("输入插入乘号的个数:"); scanf("%d", &r);
n = 0;
while(l[n] != '\0'){
//字符转换成数字
a[n] = l[n] - 48;
n++;
}
//边界初始化
for (i = 0; i < n - r; i++)
{
for (d = 0, j = 0; j <= i; j++)
d = d * 10 + a[j];
f[i][0] = d;
}
//递推
for (j = 1; j <= r; j++)
{
for (i = j; i < n - r + j; i++)
{
for (k = j; k < i; k++)
{
for (d = 0, m = k + 1; m <= i; m++)
d = d * 10 + a[m];
if (f[i][j] < f[k][j-1] * d)
{
f[i][j] = f[k][j-1] * d;
//保存乘号的位置
c[i][j] = k;
}
}
}
}
//打印最优解
b[r] = c[n-1][r];
for(j = r - 1; j > 0; j--)
b[j] = c[b[j+1]][j];
for (j = 1, i = 0; j <= r; j++)
{
while (i <= b[j])
printf("%c", l[i++]);
printf(" * ");
}
printf(" = %0.1f\n", f[n-1][r]);
}
参考资料:
1. 数据结构 : C语言版/ 严蔚敏,吴伟民编著
=============================================================================================
Linux应用程序、内核、驱动开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。