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