凸多边形最优三角剖析_动态规划

一、  问题描述

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

最优剖析:给定凸多边形的三角剖分,使得该三角剖分中诸三角形上权之和最小(是所有的三角形的权值之和,不是只计算边和弦的权值之和)。

二、  解题思路及所选算法策略的可行性分析

基本思路:动态规划。

最优子结构:若凸(n+1)边形p={V0,V1,…,VN}的最优三角剖分T包含三角形V0VkVn, 1<=k<n。则T的权为3个部分权的和:三角形V0VkVn的权,子多边形{V0,V1,…,Vk}和{Vk,Vk+1,…,Vn}的权之和。

可断言,有T确定的这2个子多女性的三角剖析也是最优。因为若有更小的三角剖析将导致T不是最优的三角剖析的矛盾

     

递归结构:定义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]。

T[1][n]=min{t[1][k]+t[k+1][n]+w(V0VkVn)|1<=k<n}

设退化的多边形{Vi-1,Vi}具有的权值为0,因为此刻够不成多边形。

三、  伪代码描述及复杂度分析

伪代码

Public class triangle{

    初始化各点两两相连权重;

    初始化所有二顶点多边形权值为0;

    //循环求解t[i][j]

    For(子多边形的始末顶点间隔步长){

       For(起始点位置){

           该层循环起始点对应末点的位置;

           求解t[i][j];

           记录第三点位置;

           找到最小权值位置;

       }

    }

}

复杂度分析

对于n规模的问题,算法需要申请n*n空间的二位数组,所以空间复杂度为O(n^2)。求解问题用到三层for循环,所以时间复杂度为O(n^3)。

四、  代码实现

package 动态规划;

public class triangle {

  private int n;// n多边形
  private int[][] weight;// 边的权值数组

  public triangle(int n) {
    this.n = n;
    this.weight = new int[n][n];
  }

  public static void main(String[] args) {
	triangle triangulation = new triangle(6);
    initTriangulation(triangulation);
    int n = triangulation.getN();// 凸多边形的边数
    int[][] t = new int[n][n];// t[i][j]表示顶点{Vi-1,Vi...Vj}组成的多边形的最优三角形剖分的权值
    int[][] s = new int[n][n];// s[i][j]表示与Vi-1和Vj一起构成三角形的第三个顶点的位置
    triangulation.minWeightTriangulation2(triangulation.getN() - 1, t, s);
    System.out.println(t[1][5]);
  }

  // 初始化weight数组的信息
  public static void initTriangulation(triangle triangulation) {
    int[][] weight = { { 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 } };
    triangulation.setWeight(weight);
  }

  // 得到最优的三角形剖分,n是总边数-1
  public void minWeightTriangulation(int n, int[][] t, int[][] s) {
    // 初始化所有的二顶点多边形权值为0
    for (int i = 1; i <= n; i++) {
      t[i][i] = 0;
    }
    // 循环求解t[i][j]
    for (int r = 2; r <= n; r++) {// (j-i)的范围[2,n]
      // 当r=2时,循环实际上是在给t赋边的值,即相邻的两个顶点的权值,例如t[1][2],t[2][3]...
      for (int i = 1; i <= n - r + 1; i++) {// i的范围[1,n+1-r],这里i要保证i+r<=n
        int j = i + r - 1;
        t[i][j] = t[i + 1][j] + getWeight(i - 1, i, j);// 这里实际上就是k=i
        // t[i][j] = t[i][i] + t[i + 1][j] + getWeight(i - 1, i, j)
        s[i][j] = i;
        // i-1,i,j
        // 循环k,范围是[i+1,j-1],求出最小的t[i][j]
        for (int k = i + 1; k < j; k++) {// k是i和j之间的中间顶点
          int u = t[i][k] + t[k + 1][j] + getWeight(i - 1, k, j);// 以k作为划分得到的权值
          if (u < t[i][j]) {// 如果权值更小,那么同时更新t[i][j]和s[i][j]
            t[i][j] = u;
            s[i][j] = k;
          }
        }
      }
    }
  }

  // 我的写法,在第二个循环这里不同,没有什么差别,只是我易于我理解
  public void minWeightTriangulation2(int n, int[][] t, int[][] s) {
    // 初始化所有的二顶点多边形权值为0
    for (int i = 1; i <= n; i++) {
      t[i][i] = 0;
    }
    // 循环求解t[i][j]
    for (int r = 1; r <= n; r++) {// r=(j-i)的范围[1,n]
      // 当r=1时,循环实际上是在给t赋边的值,即相邻的两个顶点的权值,例如t[1][2],t[2][3],t[3][4]...
      for (int i = 1; i <= n - r; i++) {// i的范围[1,n-r],这里i要保证 j=i+r<=n
        int j = i + r;
        t[i][j] = t[i + 1][j] + getWeight(i - 1, i, j);// 这里实际上就是k=i
        // t[i][j] = t[i][i] + t[i + 1][j] + getWeight(i - 1, i, j)
        s[i][j] = i;// i-1,i,j
        // 循环k,范围是[i+1,j-1],求出最小的t[i][j]
        for (int k = i + 1; k < j; k++) {// k是i和j之间的中间顶点
          int u = t[i][k] + t[k + 1][j] + getWeight(i - 1, k, j);// 以k作为划分得到的权值
          if (u < t[i][j]) {// 如果权值更小,那么同时更新t[i][j]和s[i][j]
            t[i][j] = u;
            s[i][j] = k;
          }
        }
      }
    }
  }

  // 计算一个三角形的权值之和
  public int getWeight(int i, int j, int k) {
    return weight[i][j] + weight[j][k] + weight[i][k];
  }

  public int getN() {
    return n;
  }

  public void setN(int n) {
    this.n = n;
  }

  public int[][] getWeight() {
    return weight;
  }

  public void setWeight(int[][] weight) {
    this.weight = weight;
  }

}

五、  代码运行结果截图

 

原文地址:https://www.cnblogs.com/LieYanAnYing/p/11817166.html