动态规划 dp05 插入乘号问题 c代码

先看题目:

在一个由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),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值