凸多边形最优三角形

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



 

 程序清单如下:

[cpp] view plain copy
//3d5 凸多边形最优三角剖分
#include “stdafx.h”
#include
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];
}
程序输入如下所示:

 运行结果如图:

0015算法笔记——【动态规划】多边形游戏问题
分类: 算法2013-03-06 11:08 562人阅读 评论(0) 收藏 举报
动态规划 算法笔记 多边形游戏 最优子结构
1、问题描述:

  给定N个顶点的多边形,每个顶点标有一个整数,每条边上标有+(加)或是×(乘)号,并且N条边按照顺时针

依次编号为1~N。下图给出了一个N=4个顶点的多边形。

 游戏规则 :(1) 首先,移走一条边。 (2) 然后进行下面的操作: 选中一条边E,该边有两个相邻的顶点,不妨称为V1和V2。对V1和V2顶点所标的整数按照E上所标运算符号(+或是×)进行运算,得到一个整数;用该整数标注一个新顶点,该顶点代替V1和V2 。 持续进行此操作,直到最后没有边存在,即只剩下一个顶点。该顶点的整数称为此次游戏的得分(Score)。





2、问题分析:

 解决该问题可用动态规划中的最优子结构性质来解。

设所给的多边形的顶点和边的顺时针序列为op[1],v[1],op[2],v[2],op[3],…,op[n],v[n] 其中,op[i]表示第i条边所对应的运算符,v[i]表示第i个顶点上的数值,i=1~n。

在所给的多边形中,从顶点i(1<=i<=n)开始,长度为j(链中有j个顶点)的顺时针链p(i,j)可表示为v[i],op[i+1],…,v[i+j-1],如果这条链的最后一次合并运算在op[i+s]处发生(1<=s<=j-1),则可在op[i+s]处将链分割为两个子链p(i,s)和p(i+s,j-s)。

设m[i,j,0]是链p(i,j)合并的最小值,而m[i,j,1]是最大值。若最优合并在op[i+s]处将p(i,j)分为两个长度小于j的子链的最大值和最小值均已计算出。即:

a=m[i,s,0]  b=m[i,s,1]  c=m[i,s,0]  d=m[i,s,1]

(1) 当op[i+s]=’+’时

m[i,j,0]=a+c ;m[i,j,1]=b+d

(2) 当op[i+s]=’*’时

m[i,j,0]=min{ac,ad,bc,bd} ; m[i,j,1]=max{ac,ad,bc,bd}

由于最优断开位置s有1<=s<=j-1的j-1中情况。 初始边界值为 m[i,1,0]=v[i]   1<=i<=n m[i,1,1]=v[i]   1<=i<=n

因为多变形式封闭的,在上面的计算中,当i+s>n时,顶点i+s实际编号为(i+s)modn。按上述递推式计算出的m[i,n,1]记为游戏首次删除第i条边后得到的最大得分。

  算法具体代码如下:

[cpp] view plain copy
//3d6 多边形游戏
#include “stdafx.h”
#include
using namespace std;

#define NMAX 100
int N,m[NMAX+1][NMAX+1][2],v[NMAX+1];
char op[NMAX+1];

void MinMax(int n,int i,int s,int j,int &minf,int &maxf);
int PloyMax(int n,int& p);

int main()
{
int p;
cout<<“请输入多边形顶点数:”<<endl;
cin>>N;
for(int i=1; i<=N; i++)
{
cout<<“请输入多边形顶点”<<i<<“数值:”<<endl;
cin>>v[i];
m[i][1][0]=v[i];
m[i][1][1]=v[i];
cout<<“请输入多边形边”<<i<<“运算符:”<<endl;
cin>>op[i];
}
cout<<“多边形游戏首次删除第”<<p<<“条边,结果为:”<<PloyMax(N,p)<<endl;
return 0;
}

void MinMax(int n,int i,int s,int j,int &minf,int &maxf)
{
int e[5];
int a=m[i][s][0],b=m[i][s][1];
int r=(i+s-1)%n+1;//多边形的实际顶点编号
int c=m[r][j-s][0],d=m[r][j-s][1];

if(op[r-1]=='+')  
{     
    minf=a+c;  
    maxf=b+d;  
}   
else  
{     
    e[1]=a*c;  
    e[2]=a*d;  
    e[3]=b*c;   
    e[4]=d*b;    
    minf=e[1];    
    maxf=e[1];   

    for(int r=2;r<N;r++)   
    {     
        if(minf>e[r])minf=e[r];  
        if(maxf<e[r])maxf=e[r];  
    }  
}  

}

int PloyMax(int n,int& p)
{
int minf,maxf;
for(int j=2;j<=n;j++) //迭代链的长度
{
for(int i=1;i<=n;i++)//迭代首次删掉第i条边
{
for(int s=1 ;s<j;s++) //迭代断开位置
{
MinMax(n,i,s,j,minf,maxf);
if(m[i][j][0]>minf) m[i][j][0]=minf;
if(m[i][j][1]<maxf) m[i][j][1]=maxf;
}
}
}

int temp=m[1][n][1];   
p=1;  

for(int i=2 ;i<=n; i++)    
{      
    if(temp<m[i][n][1])   
    {  
        temp=m[i][n][1];  
        p=i;  
    }  
}                    
return temp;  

}
程序运行结果如下:

分享到:
上一篇:0014算法笔记——【动态规划】凸多边形最优三角剖分
0016算法笔记——【动态规划】图像压缩问题
分类: 算法2013-03-07 22:00 501人阅读 评论(0) 收藏 举报
图像压缩 动态规划 算法笔记 最优子结构
1、问题描述:

 在计算机中,常用像素点的灰度值序列{p1,p1,……pn}表示图像。其中整数pi,1<=i<=n,表示像素点i的灰度值。通常灰度值的范围是0~255。因此最多需要8位表示一个像素。

  压缩的原理就是把序列{p1,p1,……pn}进行设断点,将其分割成一段一段的。分段的过程就是要找出断点,让一段里面的像素的最大灰度值比较小,那么这一段像素(本来需要8位)就可以用较少的位(比如7位)来表示,从而减少存储空间。

 b代表bits,l代表length,分段是,b[i]表示每段一个像素点需要的最少存储空间(少于8位才有意义),l[i]表示每段里面有多少个像素点,s[i]表示从0到i压缩为一共占多少存储空间。

 如果限制l[i]<=255,则需要8位来表示l[i]。而b[i]<=8,需要3位表示b[i]。所以每段所需的存储空间为l[i]*b[i]+11位。假设将原图像分成m段,那么需要位的存储空间。

  图像压缩问题就是要确定像素序列{p1,p1,……pn}的最优分段,使得依此分段所需的存储空间最小。

 2、最优子结构性质

  设l[i],b[i],1<=i<=m是{p1,p1,……pn}的一个最优分段,则l[1],b[1]是{p1,……,pl[1]}的一个最优分段,且l[i],b[i],2<=i<=m是{pl[1]+1,……,pn}的一个最优分段。即图像压缩问题满足最优子结构性质。

 3、递推关系

  设s[i],1<=i<=n是像素序列{p1,p1,……pi}的最优分段所需的存储位数,则s[i]为前i-k个的存储位数加上后k个的存储空间。由最优子结构性质可得:

,式中

     4、构造最优解

 数组l[i],b[i]记录了最优分段所需的信息最优分段的最后一段的段长度和像素位数分别存储在l[n]和b[n]中,其前一段的段长度和像素位数存储于l[n-l[n]]和b[n-l[n]]中,依此类推,可在O(n)时间内构造最优解。

 算法具体实现代码如下:

[cpp] view plain copy
//3d7 动态规划 图像压缩问题
#include “stdafx.h”
#include
using namespace std;

const int N = 7;

int length(int i);
void Compress(int n,int p[],int s[],int l[],int b[]);
void Tracebace(int n,int& i,int s[],int l[]);
void Output(int s[],int l[],int b[],int n);

int main()
{
int p[] = {0,10,12,15,255,1,2};//图像灰度数组 下标从1开始计数
int s[N],l[N],b[N];

cou
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值