UVA-1331Minimax Triangulation

来源:互联网 发布:阿里云青岛机房位置 编辑:程序博客网 时间:2024/05/21 11:37

Brief  description 

Algorithm  analyse

区间dp与几何联系的题目,

dp过程:区间dp,dp[i][j]表示的区间i到j的最大面积的最小值。

我的dp的思路是模仿LRJ的模板,枚举左节点,枚举右节点。然后枚举中间的节点。

一个技巧。在枚举的过程中初始化,这题2个相邻节点,即同一条边的相邻节点的值为0,

然后再进行初始化为INF,判断是i,j,k是否能构成满足条件的三角形.

满足条件。 dp[i][j]=min(dp[i][j],max(max(dp[i][k],dp[k][j]),area(p[i],p[k],p[j])));

返回   dp[0][n-1]即可。


判断是i,j,k是否能构成满足条件的三角形。

计算几何问题,注意精度问题。

3个点用叉积求三角形面积。3条件用海伦公式求三角形面积。

关于叉积,我参考LRJ的代码时,他就是用叉积来做的。里面有几点判断是否满足构成三角形,我看不懂。

然后参考了别人的博客,他说只需判断是否在三角里面即可。这点我也不理解。


首先贴上与叉积有关的概念和闭包代码。

叉积

然后是参考的博客

参考博客


Code

LRJ的代码

// UVa1331 Minimax Triangulation// Rujia Liu#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<vector>#include<queue>#include<algorithm>using namespace std;const double eps = 1e-10;int dcmp(double x) {  if(fabs(x) < eps) return 0; else return x < 0 ? -1 : 1;}struct Point {  double x, y;  Point(double x=0, double y=0):x(x),y(y) { }};typedef Point Vector;Vector operator + (const Vector& A, const Vector& B) { return Vector(A.x+B.x, A.y+B.y); }Vector operator - (const Point& A, const Point& B) { return Vector(A.x-B.x, A.y-B.y); }Vector operator * (const Vector& A, double p) { return Vector(A.x*p, A.y*p); }bool operator < (const Point& a, const Point& b) {  return a.x < b.x || (a.x == b.x && a.y < b.y);}bool operator == (const Point& a, const Point &b) {  return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;}double Dot(const Vector& A, const Vector& B) { return A.x*B.x + A.y*B.y; }double Cross(const Vector& A, const Vector& B) { return A.x*B.y - A.y*B.x; }double Length(Vector A) { return sqrt(Dot(A, A)); }bool SegmentProperIntersection(const Point& a1, const Point& a2, const Point& b1, const Point& b2) {  double c1 = Cross(a2-a1,b1-a1), c2 = Cross(a2-a1,b2-a1),  c3 = Cross(b2-b1,a1-b1), c4=Cross(b2-b1,a2-b1);  return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0;}bool OnSegment(const Point& p, const Point& a1, const Point& a2) {  return dcmp(Cross(a1-p, a2-p)) == 0 && dcmp(Dot(a1-p, a2-p)) < 0;}typedef vector<Point> Polygon;int isPointInPolygon(const Point& p, const Polygon& poly){  int n = poly.size();  int wn = 0;  for(int i = 0; i < n; i++){    const Point& p1 = poly[i];    const Point& p2 = poly[(i+1)%n];    if(p1 == p || p2 == p || OnSegment(p, p1, p2)) return -1; // 在边界上    int k = dcmp(Cross(p2-p1, p-p1));    int d1 = dcmp(p1.y - p.y);    int d2 = dcmp(p2.y - p.y);    if(k > 0 && d1 <= 0 && d2 > 0) wn++;    if(k < 0 && d2 <= 0 && d1 > 0) wn--;  }  if (wn != 0) return 1; // 内部  return 0; // 外部}const int maxn = 100 + 5;bool isDiagonal(const Polygon& poly, int a, int b) {  int n = poly.size();  for(int i = 0; i < n; i++)    if(i != a && i != b && OnSegment(poly[i], poly[a], poly[b])) return false; // 中间不能有其他点  for(int i = 0; i < n; i++)    if(SegmentProperIntersection(poly[i], poly[(i+1)%n], poly[a], poly[b])) return false; // 不能和多边形的边规范相交  Point midp = (poly[a] + poly[b]) * 0.5;  return (isPointInPolygon(midp, poly) == 1); // 整条线段在多边形内}const double INF = 1e9;double d[maxn][maxn];double solve(const Polygon& poly) {  int n = poly.size();  for(int i = 0; i < n; i++)    for(int j = 0; j < n; j++) d[i][j] = -1;  for(int i = n-2; i >= 0; i--)    for(int j = i+1; j < n; j++) {      if(i + 1 == j) d[i][j] = 0;      else if(!(i == 0 && j == n-1) && !isDiagonal(poly, i, j)) d[i][j] = INF;      else {        d[i][j] = INF;        for(int k = i+1; k < j; k++) {          double m = max(d[i][k], d[k][j]);          double area = fabs(Cross(poly[j]-poly[i], poly[k]-poly[i])) / 2.0; // triangle i-j-k          m = max(m, area);          d[i][j] = min(d[i][j], m);        }      }    }  return d[0][n-1];}int main() {  int T, n;  scanf("%d", &T);  while(T--) {    scanf("%d", &n);    double x, y;    Polygon poly;    for(int i = 0; i < n; i++) { scanf("%lf%lf", &x, &y); poly.push_back(Point(x,y)); }    printf("%.1lf\n", solve(poly));  }  return 0;}


自己写的参考博客和LRJ的代码

#include <iostream>  #include <map>  #include <algorithm>  #include <cstdio>  #include <cstring>  #include <cstdlib>  #include <vector>  #include <queue>  #include <stack>  #include <functional>  #include <set>  #include <cmath>  using namespace std;  #define pb push_back  #define PB pop_back  #define bk back()  #define fs first  #define se second  #define sq(x) (x)*(x)  #define eps (1e-10)  #define INF (0x3f3f3f3f)  #define clr(x) memset((x),0,sizeof (x))  #define cp(a,b) memcpy((a),(b),sizeof (b))    typedef long long ll;  typedef unsigned long long ull;  typedef pair<int,int> P;    const int maxn=105;int n;struct point{double x,y;void get(){scanf("%lf%lf",&x,&y);}}p[maxn];double d[maxn][maxn];double area (point a, point b, point c) {      return fabs((b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y))/2;  }  bool judge (int a, int b, int c) {      double cur = area(p[a], p[b], p[c]);      for (int i = 0; i < n; i++) {          if (i == a || i == b || i == c)              continue;          double tmp = area(p[a], p[b], p[i]) + area(p[b], p[c], p[i]) + area(p[c], p[a], p[i]);          if (fabs(tmp - cur) < eps)              return false;      }      return true;  }  double solve(){for(int i=0;i<n;i++)for(int j=0;j<n;j++) d[i][j]=-1;for(int i=n-2;i>=0;i--)for(int j=i+1;j<n;j++){if(j==i+1) d[i][j]=0;else{d[i][j]=INF;for(int k=i+1;k<j;k++){if(judge(i,k,j)){d[i][j]=min(d[i][j],max(max(d[i][k],d[k][j]),area(p[i],p[k],p[j])));}}}}return d[0][n-1];}int main(){int cas;scanf("%d",&cas);while(cas--){scanf("%d",&n);for(int i=0;i<n;i++) p[i].get();printf("%.1lf\n",solve());}return 0;}


Summary

回到之前的问题,最优矩阵链乘和最优三角剖分的不同点,现在明白了。最优矩阵链乘是的决策都是顺序话化了,而最优矩阵剖分则没有。

举个例子

比如i=5,k=10,j=20,n=40中.

这样就有多个多边形。0到5,5到10,10到20,20到40.并且在这些多边形中也是可以随意划分的,这样状态转移方程不好写。

所以定个序。0-n之间的k,然后0到k,k到n。这样,然后定个区间以及顺序就可以了。




0 0
原创粉丝点击