動態規劃DP——凸多邊形最優三角剖分

1.問題分析

 我們可以把披薩餅看作是一個凸多邊形,凸多邊形是指多邊形的任意兩點的連線均落在多邊形的內部或邊界上。

(1)什麼是凸多邊形?

如下圖所示,是一個凸多邊形

如下圖所示,不是一個凸多邊,因爲v1v3連線落在了多邊形的外部

凸多邊形不相鄰的兩個頂點的連線稱爲凸多邊形的弦

(2)什麼是凸多邊形的三角剖分?

凸多邊形的三角剖分是指將一個凸多邊形分割成互不相交的三角形的弦的集合。如下圖所示,都是三角形的剖分,三角形的剖分有很多種:

如果我們在給定凸多邊形及定義在邊,弦上的權值,即任意兩點之間定義一個數值作爲i權值,如圖所示:

三角形上權值之和是指三角形的3條邊上的權值之和:

w(vi vk vj)=|vi vk| + |vk vj | + |vi vj| 

如圖所示,w(v0v1v4)=|v0v1| + |v1v4| + |v0v4| = 22+8+5=15.

(3)什麼是凸多邊形最優三角剖分?

一個凸多邊形的三角剖分有很多種,最優三角剖分就是劃分的各三角形上權函數之和最小的三角剖分。

2.算法設計

 凸多邊形最優三角剖分滿足動態規劃的最優子結構性質,可以從自底向上逐漸推出整體的最優。

(1)確定合適的數據結構

採用二維數組g[ ][ ]記錄各個頂點之間的連接權值,二維數組m[ ][ ]存放各個子問題的最優值,二維數組s[ ][ ]存放各個子問題的最優策略。

(2)初始化

輸入頂點數n,然後依次輸入各個頂點之間的連接權值存儲在二維數組g[ ][ ]中,令n=n-1(頂點標號從v0開始),

m[i][i]=0,s[i][i]=0,其中i=1,2,3,4……,n-1。

(3)循環

  • 按照遞歸關係式計算3個頂點{v i-1,vi,vi+1}的最優三角剖分,j=i+1,將最優值存入m[i][j],同時將最優策略存入s[i][j],i=1,2,3,……,n-1。
  • 按照遞歸關係式計算4個頂點{v i-1,vi,vi+1,vi+2}的最優三角剖分,j=i+2,將最優值存入m[i][j],同時將最優策略存入s[i][j],i=1,2,3,……,n-2。
  • 以此類推,直到求出所有頂點{v0,v1,v2,……,vn}的最優三角剖分,並將最優值存入m[1][n],將最優策略記入s[1][n]

(4)構造最優解

根據最優決策信息數組s[ ][ ]遞歸構造最優解,即輸出凸多邊形的最優剖分的所有弦。s[1][n],表示凸多邊形最優三角剖分位置。

  • 如果子問題1爲空,即沒有一個頂點,說明V0Vs[1][n]是一條邊,不是弦,不需要輸出,否則,輸出該弦V0Vs[1][n]
  • 如果子問題2爲空,即沒有一個頂點,說明Vs[1][n]Vn是一條邊,不是弦,不需要輸出,否則,輸出該弦Vs[1][n]Vn
  • 遞歸構造兩個子問題{ v0,v1,v2,……,Vs[1][n] } 和 { Vs[1][n] ,v1,v2,……,vn },一直遞歸到子問題爲空停止。

3.源代碼

 

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <sstream>
#include <algorithm>
using namespace std;

const int M =1111;
int n; //頂點數
int s[M][M];//記錄最優策略二維數組
double m[M][M];//記錄最優值二維數組
double g[M][M];//記錄各頂點之間權值的二維數組

void convex_Polygon_Triangulation()
{
    for (int i=1;i<=n;i++) //初始化
    {
        m[i][i]=0;
        s[i][i]=0;
    }
    for(int d=2;d<=n;d++)//d爲問題規模,d=2實際上有三個點
    {
        for (int i=1;i<=n-d+1;i++)//控制i值
        {
            int j=i+d-1; //j值
            m[i][j]=m[i+1][j]+g[i-1][i]+g[i][j]+g[i-1][j];
            //策略爲k=i的情況
            s[i][j]=i;
            for (int k=i+1;k<j;k++) //枚舉劃分點i到j所有情況
            {
                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)
    {
        cout << "{v" << i-1 << "v" << s[i][j] << "}" << endl;
    }
    if(s[i][j]+1<j)
    {
        cout << "{v" << s[i][j] << "v" << j << "}" << endl;
    }
    print(i,s[i][j]);
    print(s[i][j]+1,j);
}

int main()
{
    int i;
    int j;
    cout << "請輸入頂點個數n:"<< endl;
    cin >> n;
    n--;
    cout << "請依次輸入各頂點的連接權值:" << endl;
    for (i=0;i<=n;++i)
    {
        for (j=0;j<=n;++j)
        {
            cin >> g[i][j];
        }
    }
    convex_Polygon_Triangulation();
    cout << "最優三角剖分的權值和是:" << endl;
    cout << m[1][n]<< endl;
    cout << "頂點的集合是:"<< endl;
    print(1,n);
    return 0;
}

 

4.測試結果

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章