转!算法设计大作业——凸多边形最优三角剖分

转载说明:本文由 风仲达 原创,如有侵权立刻删除 

     1、问题相关定义:

     (1)凸多边形的三角剖分将凸多边形分割成互不相交的三角形的弦的集合T。

    (2)最优剖分给定凸多边形P,以及定义在由多边形的边和弦组成的三角形上的权函数w。要求确定该凸多边形的三角剖分,使得该三角剖分中诸三角形上权之和为最小。

     凸多边形三角剖分如下图所示:

          2、最优子结构性质

     若凸(n+1)边形P={V0,V1……Vn}的最优三角剖分T包含三角形V0VkVn,1<=k<=n,则T的权为三个部分权之和:三角形V0VkVn的权,多边形{V0,V1……Vk}的权和多边形{Vk,Vk+1……Vn}的权之和。如下图所示:

          可以断言,由T确定的这两个子多边形的三角剖分也是最优的。因为若有{V0,V1……Vk}和{V0,V1……Vk}更小权的三角剖分,将导致T不是最优三角剖分的矛盾。因此,凸多边形的三角剖分问题具有最优子结构性质。

         3、递推关系:

     设t[i][j],1<=i<j<=n为凸多边形{Vi-1,Vi……Vj}的最优三角剖分所对应的权值函数值,即其最优值。最优剖分包含三角形Vi-1VkVj的权,子多边形{Vi-1,Vi……Vk}的权,子多边形{Vk,Vk+1……Vj}的权之和。

      因此,可得递推关系式:

     凸(n+1)边形P的最优权值为t[1][n]。

    

     程序清单如下:

//3d5 凸多边形最优三角剖分
#include <iostream> 
using namespace std; 
 
const int N = 7;//凸多边形边数+1
int weight[][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 MinWeightTriangulation(int n,int **t,int **s);
void Traceback(int i,int j,int **s);//构造最优解
int Weight(int a,int b,int c);//权函数
 
int main()
{
	int **s = new int *[N];  
    int **t = new int *[N];  
    for(int i=0;i<N;i++)    
    {    
        s[i] = new int[N];  
        t[i] = new int[N];  
    } 
 
	cout<<"此多边形的最优三角剖分值为:"<<MinWeightTriangulation(N-1,t,s)<<endl;  
    cout<<"最优三角剖分结构为:"<<endl;  
    Traceback(1,5,s); //s[i][j]记录了Vi-1和Vj构成三角形的第3个顶点的位置
 
	return 0;
}
 
int MinWeightTriangulation(int n,int **t,int **s)
{
	for(int i=1; i<=n; i++)
	{
		t[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;//计算前边界为r,链长为r的链的后边界  
 
			t[i][j] = t[i+1][j] + Weight(i-1,i,j);//将链ij划分为A(i) * ( A[i+1:j] )这里实际上就是k=i
 
			s[i][j] = i;
 
			for(int k=i+1; k<j; k++)
			{
				//将链ij划分为( A[i:k] )* (A[k+1:j])   
				int u = t[i][k] + t[k+1][j] + Weight(i-1,k,j);
				if(u<t[i][j])
				{
					t[i][j] = u;
					s[i][j] = k;
				}
			}
		}
	}
	return t[1][N-2];
}
 
void Traceback(int i,int j,int **s)
{
	if(i==j) return;
	Traceback(i,s[i][j],s);
	Traceback(s[i][j]+1,j,s);
	cout<<"三角剖分顶点:V"<<i-1<<",V"<<j<<",V"<<s[i][j]<<endl;
}
 
int Weight(int a,int b,int c)
{
	 return weight[a][b] + weight[b][c] + weight[a][c];
}

     程序输入如下所示:

     运行结果如图:


第二个答案

转载说明:本文由 咚咚锵_光 原创,如有侵权立刻删除 

一凸8边形P的顶点顺时针为 ,任意两顶点间的线段的权重由矩阵D给出。若 与 是P上不相邻的两个顶点,则线段 称为P的一条弦。求P得一个弦的集合T,使得T中多有的弦恰好将P分割成互不重迭的三角形,且各三角形的权重之和为最小(一个三角形的权重是其各边的权重之和)。

关于三角划分的一个小例子

求解 

解题思路:

第一步:首先需要确定一个事实,这是一个无向图,假设图有5个节点,当你考虑节点顺序2,3,5构成的划分时,你也同时考虑了节点5,2,3..的划分

也就是说,当你划分程序走到序号最大的节点n之后,就不必在走一个环路,走回节点1了,因为你已经考虑过及节点1到节点n之间的最佳划分,这同时也是节点n到节点1的最佳划分

第二步:其次,要明确一点,这题是一道动态规划的题目,计算过程是一个积少成多的过程

第三步:解题:

1、我们用D[i][j]表示从节点i一直到节点j的最优划分,用w(i,j,k)计算并表示由节点i,j,k构成的三角形边的总和。

2、可以证明,肯定存在两个拓扑顺序连续的点,外加另外一个点,将此凸多边形划分为三部分,左侧多边形D1,三角形w(i,i+1,k),右侧多边形D2

什么事拓扑顺序?可能在这里将其称为拓扑有失偏颇,但是,其性质确实有些类似,当你排出掉点i之后,i-1拓扑连续的下一个点就变成了i+1,如此理解应该可以明白


既然是动态规划,那必定有一个确定的递推公式,此题的递推公式如下:

F(i,j)=min{F(i,k)+F(k,j)+w(i,j,k)}

此公式就表示了上面解题2中阐述的事实


伪码:

//初始化

 

for i←0 to n-1

     for j←0 to n-1

         if j==i+1

              D[i][j]←weight[i][j];

         else

              D[i][j]←0

for l←3 to n

     for i←0 to SIZE-l

         j←i+l-1;

         //i,i+1,j构成的三角形和i+1到j的划分来初始化D[i][j]

         D[i][j] ←w(i,i+1,j)

         if l!=3

              D[i][j]←D[i][j]+D[i+1][j]

         for k←i+2 to j-1//滑动中间节点,不断进行划分

     //左侧的划分

              m←D[i][k]+w(i,j,k)

              //若l==3说明,i=j-2即只有i,i+1,i+2三个顶点,只能构成三角形,不需要继续划分

              //l!=3说明,至少是4个连续的顶点,所以,除了i,i+1,j构成的三角形之外,还有i+1到j的一

      //个三角行划分

if k

                   m←m+D[k][j]

              if m

                   D[i][j]←m

Out Put D[SIZE][SIZE]

 

D[0][SIZE-1] is the final result 

 

有一点需要解释,这是动态规划,因此,当你划分1~7构成的多边形时,1~6,2~7都是已经划分好的

有人或许会问,为什么没有3,4,5,6,7,1这样的节点集合呢?

题目的解题思路是这样的,以<1,7>为底边,让顶点在2~6之间移动,必定存在一个点,构成三角形w(1,k,7)属于最优三角划分,因为<1,7>肯定是划分中的一条边。

寻到k之后,边<1,7>被排除,区域被划分为1~k和k~7,彼此之间不再有交集,因此不存在回路。

而后分别以边<1,k>,为底边,分别在顶点集2~k-1和k+1~6之间寻找下一个最优化分。

这里可以参考一下并查集的思路,这里其实是逆着并查集的思想不断深入的。好好思考!


因此也就不存在类如3,4,5,6,7,1的划分方式


实际测试代码

//代码、

 

#include

 

#define SIZE 8

using namespace std;

 

const int weight[8][8]={

     {0,14,25,27,10,11,24,16},

     {14,0,18,15,27,28,16,14},

     {25,18,0,19,14,19,16,10},

     {27,15,19,0,22,23,15,14},

     {10,27,14,22,0,14,13,20},

     {11,28,19,23,14,0,15,18},

     {24,16,16,15,13,15,0,27},

     {16,14,10,14,20,18,27,0}

};

int w(int x,int y,int z){

     return weight[x][y]+weight[y][z]+weight[z][x];

}

 

int main(){

     int D[SIZE][SIZE];

     for(int i=0;i

         for(int j=0;j

              D[i][j]=0;

              if(j==i+1)D[i][j]=weight[i][j];

         }

     for(int l=3;l<=SIZE;l++){

         for(int i=0;i<=SIZE-l;i++){

              int j=i+l-1;

              //初始化一个值

              D[i][j]=w(i,i+1,j);

              //若l==3说明,i=j-2即只有i,i+1,i+2三个顶点,只能构成三角形,不需要继续划分

              //l!=3说明,至少是4个连续的顶点,所以,除了i,i+1,j构成的三角形之外,还有i+1到j的一

//个三角行划分     

if(l!=3){

                   D[i][j]+=D[i+1][j];

              }

              //初始化结束

              //寻找最小值

              for(int k=i+2;k

                   int m = D[i][k]+w(i,j,k);

                   //若k说明右侧还有大于等于连续的3个顶点,需要加上右侧划分

if(k

                       m+=D[k][j];

                   if(m

                       D[i][j]=m;

              }

         }

     }

     for(int i=0;i

         for(int j=0;j

              cout<<D[i][j]<<'\t';

         cout<<endl;

     }

}


谨以此为备忘,读者有不明白的地方请留言,嘿嘿

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值