问题描述:
给一个有n个顶点的凸多边形,有很多方法进行三角剖分(polygon triangulation) 。给每个三角形规定一个权函数w(i,j,k)(比如三角形的周长或者三顶点的权和或者三角形的面积等等),求让所有三角形权和最大的方案。
分析:
这个问题的关键在于状态的定义,因为如果允许随意切割,显然任意“半成品” 多边形的各个顶点可以是原多边形中随意选取的,很难简洁的定义成状态。但我们又可以发现,对于同一种切割方法,我们可以有多种切割顺序,但切割方法就已经决定了这个方法产生的结果,我们只要计算其中一种切割顺序就好了,所以不如把决策顺序规范化。
定义 d[i,j] 为从顶点 i 开始到顶点 j 所构成的多边形的最大三角剖分权和,则边 i→j 在此多边形内一定恰好属于一个三角形 i→j→k ,所以多边形就被分成了三部分:▲ikj 及其左右两部分子多边形。这个切割方法保证了两个子多边形它们的顶点编号是连续的。所以我们只需要每次将代表这个多边形的两个顶点(始、末)作为分割后的其中一个三角形的一条边,然后枚举第三个顶点 k 的情况,再依次分割下去直到分割完成。
所以,有状态转移方程:d[i,j] = max{d[i,k]+d[k,j]+w(i,j,k)},时间复杂度为O(n3)
结合一道具体的题目来加深一下理解
题目链接:
https://cn.vjudge.net/problem/UVA-1331
题意:
给定一个n边形,要你求将这个n边形分割成若干三角形后,其中面积最大的三角形的最小值
分析:
典型的最优三角剖分问题,状态转移方程:d[i][j] = min{max{SΔijk,d[i][k],d[k][j]}} (i<k<j)
不过这里需要注意的是,这道题并没有指明这个多边形的凹凸性,所以对于凹多边形的情况,需要加上一个判断,即判断Δijk是否是合法的三角形,如果Δijk内部含有多边形的一个顶点,那么就是不合法的
具体分析细节,这篇博客写的不错https://blog.csdn.net/c20190102/article/details/75418824
代码:
1 #include<iostream> 2 #include<stdio.h> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 const int maxn = 60; 7 const int INF = 0x3f3f3f3f; 8 int x[maxn], y[maxn]; 9 double dp[maxn][maxn]; 10 int n; 11 double area(int a, int b, int c) 12 { 13 double ans; 14 ans = (x[a] * y[b] + x[b] * y[c] + x[c] * y[a] - x[a] * y[c] - x[b] * y[a] - x[c] * y[b]) * 1.0 / 2; 15 return abs(ans); 16 } 17 18 int judge(int i, int j, int k) 19 { 20 for(int v = 0; v < n; v++) 21 { 22 if(v == i || v == j || v == k) 23 continue; 24 else 25 { 26 double sum = area(i, j, v) + area(i, k, v) + area(j, k, v); 27 if(abs(sum - area(i, j, k)) <= 0.001) //因为有浮点数误差,这里初略认为小于0.001即为相等 28 return 0; 29 } 30 } 31 return 1; 32 } 33 int main() 34 { 35 int t; 36 cin >> t; 37 while(t--) 38 { 39 memset(dp, 0, sizeof(dp)); 40 cin >> n; 41 for(int i = 0; i < n; i++) 42 { 43 cin >> x[i] >> y[i]; 44 } 45 //i<k<j 注意边界 46 for(int i = n - 3; i >= 0; i--) 47 { 48 for(int j = i + 2; j <= n - 1; j++) 49 { 50 dp[i][j] = INF; 51 for(int k = i + 1; k < j; k++) 52 { 53 if(judge(i, j, k)) 54 dp[i][j] = min(dp[i][j], max(area(i, j, k), max(dp[i][k], dp[k][j]))); 55 } 56 } 57 } 58 printf("%.1lf ", dp[0][n - 1]); 59 } 60 return 0; 61 }