动态规划-最优三角剖分

凸多边形的三角剖分:将一个凸多边形分割成互不相交的三角形的弦的集合。

最优三角剖分:划分的各三角形上权函数之和最小的三角剖分。

证明是否具有最优子结构性质:

(1)分析最优解的结构特征

假设在第k个顶点切开会得到最优解,那么原问题就变成了两个子问题一个三角形,子问题分别是\{v_0,v_1,...,v_k\}\{v_k,v_{k+1},...,v_n\},三角形为v_0v_kv_n

证明:原问题的最优解包含子问题的最优解。

假设\{v_0,v_1,...,v_n\}三角剖分的权值之和是c,\{v_0,v_1,...,v_k\}三角剖分的权值之和是a,\{v_k,v_{k+1},...,v_n\}三角剖分的权值之和是b,三角形v_0v_kv_n的权值之和是w(v_0v_kv_n),那么c=a+b+w(v_0v_kv_n)。因此证明如果c是最优的,则a和b一定是最优的。

反证法:

如果a不是最优的,\{v_0,v_1,...,v_k\}三角剖分一定存在一个最优解a',a'<a,那么a'+b+w(v_0v_kv_n)<c,所以c不是最优的,这与假设c是最优的矛盾。因此,如果c是最优的,则a和b一定是最优的。

(2)建立最优值的递归式

用m[i][j]表示凸多边形\{v_{i-1},vi,...,vj\}三角剖分的最优值,那么两个子问题\{v_{i-1},v_i,...,v_k\}\{v_k,v_{k+1},...,v_j\}对应的最优值分别是m[i][k]、m[k+1][j],剩下的就是三角形v_{i-1}v_kv_j的权值之和是w(v_{i-1}v_kv_j)

凸多边形三角剖分最优解递归式:

(3)自底向上计算并记录最优值

先求只有3个顶点凸多边形三角剖分的最优值,再求4个,直到n个。

(4)构造最优解

需要从记录表中还原部分次序,找到最优剖分的弦,由这些弦构造出最优解。

算法设计:

(1)确定合适的数据结构

采用g[][]记录各个顶点之间的连接权值,m[][]存放各个子问题的最优值,s[][]存放各个子问题的最优策略

(2)初始化

令n=n-1(顶点标号从v0开始),m[i][i]=0,s[i][i]=0。

(3)循环阶段

按照递归关系式计算3个顶点\{v_{i-1},v_i,v_{i+1}\}的最优三角剖分,j=i+1,将最优值存入m[i][j],同时最优策略记入s[i][j]。

以此类推,直到求出所有顶点\{v_0,v_1,...,v_n\}的最优三角剖分,并将最优值存入m[1][n],将最优策略记入s[1][n]。

(4)构造最优解

根据最优决策信息数组s[][]递归构造最优解,即输出凸多边形最优剖分的所有弦。s[1][n]表示凸多边形\{v_0,v_1,...,v_n\}的最优三角剖分位置。

  • 如果子问题1为空,即没有一个顶点,说明v_0v_{s[1][n]}是一条边,不是弦,不需输出,否则输出。
  • 如果子问题2为空,即没有一个顶点,说明v_{s[1][n]}v_n是一条边,不是弦,不需输出,否则输出。
  • 递归构造两个子问题,直到子问题为空。

示例代码:

#include <iostream>
#include <sstream>
#include <cmath>
#include <algorithm>
using namespace std;
const int M=1000+5;
int n;
int s[M][M];
double m[M][M],g[M][M];
void Convexpolygontriangulation(){
    for(int i=1;i<=n;i++){
        m[i][i]=0;
        s[i][i]=0;
    }
    for(int d=2;d<=n;d++)
        for(int i=1;i<=n-d+1;i++){
            int j=i+d-1;
            m[i][j]=m[i+1][j]+g[i-1][i]+g[i][j]+g[i-1][j]; //子问题2 m[i+1][j]
            s[i][j]=i;
            for(int k=i+1;k<j;k++){
                double temp=m[i][k]+m[k+1][j]+g[i-1][k]+g[k][j]+g[i-1][j];
                if(m[i][j]>temp){
                    m[i][j]=temp;
                    s[i][j]=k;
                }
            }
        }
}

void print(int i,int j){
    if(i==j) return;
    if(s[i][j]>i) //表示i到s[i][j]之间存在顶点,子问题1不为空
        cout<<"{v"<<i-1<<"v"<<s[i][j]<<"}"<<endl;
    if(j>s[i][j]+1) //表示s[i][j]+1到j之间存在顶点,子问题2不为空
        cout<<"{v"<<s[i][j]<<"v"<<j<<"}"<<endl;
    print(i,s[i][j]);
    print(s[i][j]+1,j);
}

猜你喜欢

转载自blog.csdn.net/qq_27437197/article/details/87657555
今日推荐