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

/*
  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][n]。
  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][n],可以用通过由下至上的,从链长(多边形的边)为2开始计算,每次求m[i][j]的最小值,
并且记录最小值所对应的K值,根据最优子结构的性质,逐步向上就可以求出m[0][n]的最小值。
*/
#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[N][N] = {{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[N][N];    //m[i][j]表示多边形{Vi-1VkVj}的最优权值
int s[N][N];    //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[n][n] = 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][n];
}

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][n]。

需要注意的是,我们在求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[N][N] = {{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[N][N];    //m[i][j]表示多边形{Vi-1VkVj}的最优权值
int s[N][N];    //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][n];
}

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);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值