出处不详 凸多边形最优三角剖分
题目描述
背景
给定 n n n边凸多边形,要求确定该凸多边形的三角剖分(将多边形分割成 n − 2 n - 2 n−2个三角形),使得该三角剖分中诸三角形的权值和最小(三角形的权值等于三条边权值相加)
输入
- 第一行一个整数 n n n,表示凸多边形的边数;
- 接下来 n n n行,其中第 i i i行输入 n − i + 1 n - i + 1 n−i+1个数,第 j j j个数表示顶点 i i i到顶点 i + j − 1 i + j - 1 i+j−1的边或弦的权值
输出
一个整数,表示最小权值和
数据范围
3 ≤ n ≤ 8 3 \le n \le 8 3≤n≤8
题解
假设弦
(
i
,
j
)
(i , j)
(i,j)必须连接(不妨令
i
<
j
i < j
i<j),考虑它与它某一侧围成的多边形(不妨取顶点编号在
i
,
j
i , j
i,j之间的那一侧),设为
A
i
,
j
A_{i , j}
Ai,j,在
A
i
,
j
A_{i , j}
Ai,j中,弦
(
i
,
j
)
(i , j)
(i,j)一定参与且仅参与构成了一个三角形,因为已知三角形的两个顶点
i
,
j
i , j
i,j,所以枚举这个三角形时只需要枚举剩下的那个顶点,设为
k
k
k(
i
<
k
<
j
i < k < j
i<k<j)
定义二维数组
f
,
v
a
l
f,val
f,val,
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示
A
i
,
j
A_{i , j}
Ai,j的最优三角剖分权值和,
v
a
l
[
i
]
[
j
]
val[i][j]
val[i][j]表示边或弦
(
i
,
j
)
(i , j)
(i,j)的权值(
i
<
j
i < j
i<j),则状态转移方程为
f
[
i
]
[
j
]
=
min
(
f
[
i
]
[
j
]
,
f
[
i
]
[
k
]
+
f
[
k
]
[
j
]
+
v
a
l
[
i
]
[
k
]
+
v
a
l
[
k
]
[
j
]
+
v
a
l
[
i
]
[
j
]
)
f[i][j] = \min(f[i][j] , f[i][k] + f[k][j] + val[i][k] + val[k][j] + val[i][j])
f[i][j]=min(f[i][j],f[i][k]+f[k][j]+val[i][k]+val[k][j]+val[i][j])
考虑一开始弦
(
i
,
j
)
(i , j)
(i,j)的极端情况,即边
(
i
,
j
)
(i , j)
(i,j),这样最后的答案就变成了
f
[
1
]
[
n
]
f[1][n]
f[1][n]
代码如下:
#include<cstdio>
#include<cstring>
#define il inline
const int M = 1e2 + 5;
il int read() {
int x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return x;
}
int n, val[M][M], f[M][M];
il int getL(int a, int b, int c) { //求三个顶点所围三角形的权值
return val[a][b] + val[b][c] + val[a][c];
}
int main() {
n = read();
memset(f, 0x7f, sizeof(f));
for(int i = 1; i <= n; ++i) {
for(int j = i; j <= n; ++j)
val[i][j] = read();
f[i][i + 1] = 0; //防止更新时出错
}
for(int i = n - 2; i > 0; --i) { //因为前面的要用后面的,所以i从后往前枚举
for(int j = i + 2; j <= n; ++j) //A_{i , j}至少要是一个三角形
for(int k = i + 1; k < j; ++k) {
int tmp = f[i][k] + f[k][j] + getL(i, k, j);
if(tmp < f[i][j]) f[i][j] = tmp;
}
}
printf("%d", f[1][n]);
return 0;
}