您的位置:首页 > 其它

凸多边形最优三角剖分的两种算法分析

2017-03-28 15:19 375 查看
/*
Name:
Copyright:
Author: 巧若拙
Date: 27-03-17 10:11
Description: 动态规划--凸多边形最优三角剖分
题目描述:
用多边形顶点的逆时针序列表示凸多边形,即P={v0,v1,…,vn-1}表示具有n条边的凸多边形。
给定凸多边形P,以及定义在由多边形的边和弦组成的三角形上的权函数w。要求确定该凸多边形的三角剖分,
使得即该三角剖分中诸三角形上权之和为最小。
解题思路:
  若凸(n+1)边形P={v0,v1,…,vn}的最优三角剖分T包含三角形v0vkvn,1≤k≤n-1,则T的权为3个部分权的和:
三角形v0vkvn的权,子多边形{v0,v1,…,vk}和{vk,vk+1,…,vn}的权之和。
可以断言,由T所确定的这2个子多边形的三角剖分也是最优的。
因为若有{v0,v1,…,vk}或{vk,vk+1,…,vn}的更小权的三角剖分将导致T不是最优三角剖分的矛盾。
那么我们定义一个m[i][j],0<=i<=j<N,为凸子多边形{vi,vi+1,…,vj}的最优三角剖分所对应的权函数值,即其最优值。
据此定义,要计算的凸(n+1)边形P的最优权值为m[0]
。
  m[i][j]的值可以利用最优子结构性质递归地计算。当j-i>1时,凸子多边形至少有3个顶点。
由最优子结构性质,m[i][j]的值应为m[i][k]的值加上m[k][j]的值,再加上三角形vivkvj的权值,其中i<k<j。
由于在计算时还不知道k的确切位置,而k的所有可能位置只有j-i个,
因此可以在这j-i个位置中选出使m[i][j]值达到最小的位置。
由此,m[i][j]可递归地定义为:
m[i][j] = 0 (i == j || i+1 == j),只有一个或两个点,不能构造三角形
m[i][j] = min{m[i][k] + m[k][j] + w(vi,vk,vj)} (i < k < j)
对于要求的m[0]
,可以用通过由下至上的,从链长(多边形的边)为2开始计算,每次求m[i][j]的最小值,
并且记录最小值所对应的K值,根据最优子结构的性质,逐步向上就可以求出m[0]
的最小值。
*/
#include<iostream>

using namespace std;

int MinWeightTriangulation(int i, int j);//自顶向下,使用备忘录数组的动态规划算法
int MinWeightTriangulation_2(int n);//自底向上的动态规划算法
int GetWeight(int i, int k, int j);//计算三角形的周长
void PrintTriangulation(int i, int j); //输出组成最优解的各个三角形的周长

const int N = 6;
int w

= {{0,2,2,3,1,4},
{2,0,1,5,2,3},
{2,1,0,2,1,4},
{3,5,2,0,6,2},
{1,2,1,6,0,1},
{4,3,4,2,1,0}};//图的邻接矩阵
int m

;    //m[i][j]表示多边形{Vi-1VkVj}的最优权值
int s

;    //s[i][j]记录Vi-1到Vj最优三角剖分的中间点K

int main(int argc, char **argv)
{
cout << MinWeightTriangulation(0, N-1) << endl;
cout << MinWeightTriangulation_2(N-1) << endl;

PrintTriangulation(0, N-1); //输出组成最优解的各个三角形的周长

system("pause");
return 0;
}

int MinWeightTriangulation(int i, int j)//自顶向下,使用备忘录数组的动态规划算法
{
if (m[i][j] != 0) //默认为0
return m[i][j];
if (i == j || i+1 == j)
return 0;
//先处理k=i+1的情形,注意我们取顶点vi为起点
m[i][j] = MinWeightTriangulation(i+1, j) + GetWeight(i, i+1, j); //MinWeightTriangulation(i, i)==0,就不写了
s[i][j] = i+1;
//再处理i+1<k<j的情形
for (int k=i+2; k<j; k++)
{
int t = MinWeightTriangulation(i, k) + MinWeightTriangulation(k, j) + GetWeight(i, k, j);
if (t < m[i][j])
{
m[i][j] = t;
s[i][j] = k;
}
}
return m[i][j];
}

int MinWeightTriangulation_2(int n)//自底向上的动态规划算法
{
m

= 0;
for (int i=0; i<n; i++) //只有1个点或2个点均不能构成凸多边形
m[i][i] = m[i][i+1] = 0;

for (int r=2; r<n; r++)//r为当前计算的链长(子问题规模)
{
for (int i=0; i<=n-r; i++) //n-r为最后一个r链的前边界
{
int j = i + r; //计算前边界为i,链长为r的链的后边界j,注意我们取顶点vi为起点
//先处理k=i+1的情形
m[i][j] = m[i][i+1] + m[i+1][j] + GetWeight(i, i+1, j);
s[i][j] = i+1;
//再处理i+1<k<j的情形
for (int k=i+2; k<j; k++)
{
int t = m[i][k] + m[k][j] + GetWeight(i, k, j);
if (t < m[i][j])
{
m[i][j] = t;
s[i][j] = k;
}
}
//	cout << "m["<<i<<"]["<<j<<"]= " << m[i][j] << "     ";
} //cout << endl;
}

return m[0]
;
}

int GetWeight(int i, int k, int j)//计算三角形的周长
{
return w[i][k] + w[k][j] + w[j][i];
}

void PrintTriangulation(int i, int j) //输出组成最优解的各个三角形的周长
{
if (i == j || i+1 == j)
return;

PrintTriangulation(i, s[i][j]);
cout << "V" << i << "-V" << s[i][j] << "-V" << j << " = " << GetWeight(i, s[i][j], j) << endl;
PrintTriangulation(s[i][j], j);
}

算法2来自于王晓东老师编著的《计算机算法设计与分析》,它与算法1的基本思路是一样的,但做了一些简化处理,考虑到顶点数少于3个时是无法构成三角形的,算法2设置退化的多边形{vi-1,vi}具有权值为0,那我们在考虑最优解时,就不需要从顶点v0开始,而是从顶点v1开始,这样要计算的凸(n+1)边形P的最优权值为m[1]


需要注意的是,我们在求m[i][j]的时候,是取顶点vi-1为起点的,所以计算三角形的权之和(周长)时,我们计算的是GetWeight(i-1, k, j)。这和算法1是不一样的。

总的来说,算法1直观,算法2 简洁。

#include<iostream>

using namespace std;

int MinWeightTriangulation(int i, int j);//自顶向下,使用备忘录数组的动态规划算法
int MinWeightTriangulation_2(int n);//自底向上的动态规划算法
int GetWeight(int i, int k, int j);//计算三角形的周长
void PrintTriangulation(int i, int j); //输出组成最优解的各个三角形的周长

const int N = 6;
int w

= {{0,2,2,3,1,4},
{2,0,1,5,2,3},
{2,1,0,2,1,4},
{3,5,2,0,6,2},
{1,2,1,6,0,1},
{4,3,4,2,1,0}};//图的邻接矩阵
int m

; //m[i][j]表示多边形{Vi-1VkVj}的最优权值
int s

; //s[i][j]记录Vi-1到Vj最优三角剖分的中间点K

int main(int argc, char **argv)
{
cout << MinWeightTriangulation(1, N-1) << endl;
cout << MinWeightTriangulation_2(N-1) << endl;

PrintTriangulation(1, N-1); //输出组成最优解的各个三角形的周长

system("pause");
return 0;
}

int MinWeightTriangulation(int i, int j)//自顶向下,使用备忘录数组的动态规划算法
{
if (m[i][j] != 0) //默认为0
return m[i][j];
if (i == j)
return 0;
//先处理k=i的情形,注意我们取顶点vi-1为起点
m[i][j] = MinWeightTriangulation(i+1, j) + GetWeight(i-1, i, j); //MinWeightTriangulation(i, i)==0,就不写了
s[i][j] = i;
//再处理i<k<j的情形
for (int k=i+1; k<j; k++)
{
int t = MinWeightTriangulation(i, k) + MinWeightTriangulation(k+1, j) + GetWeight(i-1, k, j);
if (t < m[i][j])
{
m[i][j] = t;
s[i][j] = k;
}
}
return m[i][j];
}

int MinWeightTriangulation_2(int n)//自底向上的动态规划算法
{
for (int i=1; i<=n; i++)
m[i][i] = 0;

for (int r=2; r<=n; r++)//r为当前计算的链长(子问题规模)
{
for (int i=1; i<=n-r+1; i++) //n-r+1为最后一个r链的前边界
{
int j = i + r - 1; //计算前边界为i,链长为r的链的后边界j,注意我们取顶点vi-1为起点
//先处理k=i的情形
m[i][j] = m[i][i] + m[i+1][j] + GetWeight(i-1, i, j);
s[i][j] = i;
//再处理i<k<j的情形
for (int k=i+1; k<j; k++)
{
int t = m[i][k] + m[k+1][j] + GetWeight(i-1, k, j);
if (t < m[i][j])
{
m[i][j] = t;
s[i][j] = k;
}
}
}
}

return m[1]
;
}

int GetWeight(int i, int k, int j)//计算三角形的周长
{
return w[i][k] + w[k][j] + w[j][i];
}

void PrintTriangulation(int i, int j) //输出组成最优解的各个三角形的周长
{
if (i == j)
return;

PrintTriangulation(i, s[i][j]);
cout << "V" << i-1 << "-V" << s[i][j] << "-V" << j << " = " << GetWeight(i-1, s[i][j], j) << endl;
PrintTriangulation(s[i][j]+1, j);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: