动态规划 dp04 凸n边形的三角形划分 c代码

先看题目:

给定凸n边形P = {1,2,,,n},每一个顶点i带一个权数r(i)(i = 1,2,,,n)。要求在该凸n边形的顶点间连n-3条互不相交的连线,
把凸N边形分成n-2个三角形,每个三角形的值为其三个顶点权数之积。试确定一种三角形剖分,使得剖分的n-2个三角形的值之和最小。

如下图,凸6边形,顶点序号分别为1-6,顶点的权数未在图中标出。

这道题还是相当有难度的,第一次没做出来,看讲解懂了。第二次做的时候又卡在一个地方......,没理解透彻,就又看了一遍。稍微明白了。常规的动态规划类题目阶段都是很明显,比如说背包问题,每个物品就是一个阶段,但是本题目就不是那么明显了,至少对我来说。不清楚阶段没关系,继续做题,设i,j分别表示途中顶点,m[i][j]表示所划分的最大值,显而易见

m[i][i+2] = i * (i+1) * (i+2); m[i][j] = Max(m[i][k]+m[k][j]+i*j*k),其中i < k < j; 状态迁移方程是如此简洁,让多次思考没能想出的我有点怀疑智商,多看几遍也还是能够理解的,即便第一次不会,下一次遇到能够会做也行。这样的状态迁移方程和最长非降子数列问题类似。通过求解子问题,遍历得到最大值。边界情况就是m[i][i+1] = 0。利用此方法,上图的最优解是m[1][6],得到了最优解,需要知道最有子序列的值,这时候从后往前遍历顶点序列,找到最大的m[i][k],k即是分割点,接着递归遍历[i][k]和[k][j]即可。

此外的一个难点就是,虽然知道了状态迁移方程,但是要注意,求取m[i][j]的时候,需要先求出[i][k]和[k][j],这一点尤其值得注意,和普通的动态规划不一样,在设置循环的时候应该注意这点。

本题目略微有难度,看不懂的同学需要多多编码调试才行。

下面是c代码实现:

/*
 *	N凸边形划分三角形,求所得所有的三角形顶点之积的最大值
 *  m[i][j] = MAX(m[i][k] + m[k][j] + a[i]*a[k]*a[j])
 */

#include <stdio.h>


void showEdage(int (*p)[30], int i, int j)
{
	int k;
	k = p[i][j];
	if (j <= i + 1)
		return;
	if (k > i + 1)
		printf("%d - %d ", i, k);
	if (k < j - 1)	
		printf("%d - %d ", k, j);
	showEdage (p, i, k);
	showEdage (p, k, j);
}

void main()
{
	int i, j, k, d, n, a[30] = {0}, m[30][30] = {0}, c[30][30] = {0};
	printf("input nTriangle's n:");	scanf("%d", &n);
	if (n > 30)
	{
		printf("invalid n\n");
		return;
	}
	
	for (i = 0; i < n; i++)
	{
		printf("input %d value:", i+1);
		scanf("%d", &a[i]);
	}
	
	printf("you input n :%d \nvertex: ",n);
	for (i = 0; i < n; i++)
		printf("%d ", a[i]);

	//边界初始化
	for (i = 0; i < n - 1; i++)
		m[i][i+1] = 0;
	
	//递推求解
	for (d = 2; d < n; d++)
	for (i = 0; i <= n - d - 1; i++)
	{
		j = i + d;
		m[i][j] = 10000000;
		for (k = i + 1; k < j; k++)
		{
			if (m[i][j] > m[i][k] + m[k][j] + a[i] * a[j] * a[k])
			{
				m[i][j] = m[i][k] + m[k][j] + a[i] * a[j] * a[k];
				c[i][j] = k;
			}			
		}		
	}

	//打印最优解
	printf("Min : %d\n", m[0][n-1]);
	showEdage (c, 0, n-1);
}

参考资料:

1. 数据结构 : C语言版/ 严蔚敏,吴伟民编著

=============================================================================================

Linux应用程序、内核、驱动开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值