SRM 225 DIV1 Hard Triangulation
問題
(凸とは限らない)単純多角形の三角形分割を、辞書順最小でしろ。
引かれた対角線と多角形は、始点・終点以外で交差や触れてはならない
解答
まず、適当に対角線をひいても、三角形分割ができなくなることはない。つまり単純な辞書順greedyでおk。
あとは適当に「既に引いた対角線と交差せず」「多角形自身と交差せず」「多角形の内側にある」かどうかを判定したい。
これは整数幾何でがんばる
コメント
コードのコメントのとおり、「多角形の内側にある」判定で適当なことをしているが、Practice Roomのrng_58さんの解答では面積に着目しているようだ。
コード
#include <string> #include <vector> #include <algorithm> #include <sstream> #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define reu(i,l,u) for(int (i)=(int)(l);(i)<(int)(u);++(i)) #define each(it,o) for(__typeof((o).begin()) it = (o).begin(); it != (o).end(); ++ it) #define pb(x) push_back(x) #define mp(x,y) make_pair((x),(y)) using namespace std; typedef vector<pair<int,int> > vpii; typedef long long ll; typedef vector<string> vs; struct P { typedef int T; typedef ll T2; //T2は{a*b | a:T, b:T}を含むタイプ T x, y; P(T x_, T y_): x(x_), y(y_) {} P(): x(0), y(0) {} }; inline bool operator==(const P& a, const P& b) { return a.x == b.x && a.y == b.y; } inline P operator+(const P& a, const P& b) { return P(a.x+b.x, a.y+b.y); } inline P operator-(const P& a, const P& b) { return P(a.x-b.x, a.y-b.y); } inline P::T2 cross(const P& a, const P& b) { return (P::T2)a.x*b.y - (P::T2)a.y*b.x; } inline P::T2 dot(const P& a, const P& b) { return (P::T2)a.x*b.x + (P::T2)a.y*b.y; } struct L { P a, b; L(const P &a_, const P &b_): a(a_), b(b_) {} const P& operator[](size_t i) const { return i ? b : a; } }; inline int ccw(const P& a, const P& b, const P& c) { int ax = b.x - a.x, ay = b.y - a.y, bx = c.x - a.x, by = c.y - a.y; P::T2 t = (P::T2)ax*by - (P::T2)ay*bx; if (t > 0) return 1; if (t < 0) return -1; if((P::T2)ax*bx + (P::T2)ay*by < 0) return +2; if((P::T2)ax*ax + (P::T2)ay*ay < (P::T2)bx*bx + (P::T2)by*by) return -2; return 0; } int intersectSS(const L &s, const L &t) { int a = ccw(s.a,s.b,t.a), b = ccw(s.a,s.b,t.b), c = ccw(t.a,t.b,s.a), d = ccw(t.a,t.b,s.b); int x = a * b, y = c * d; if(x == -1 && y == -1) return 0; // cross else if( ((abs(b) == 1 || b == -2) && s.b == t.a) || ((abs(b) == 1 || b == 2) && s.a == t.a) || ((abs(a) == 1 || a == -2) && s.b == t.b) || ((abs(a) == 1 || a == 2) && s.a == t.b) ) return 2; // point overlap else if( (x == 0 && (abs(a) + abs(b) == 1)) || (y == 0 && (abs(c) + abs(d) == 1)) ) return 2; // point overlap else if(x <= 0 && y <= 0) return 0; //line overlap else return 1; } bool contains(const vector<P>& o, const P& p) { bool in = false; for (int i = 0; i < o.size(); ++i) { P a = o[i] - p, b = o[(i+1)%o.size()] - p; if (a.y > b.y) swap(a, b); if (a.y <= 0 && 0 < b.y) if (cross(a, b) < 0) in = !in; if (cross(a, b) == 0 && dot(a, b) <= 0) return true; //point on edge } return in; } struct Triangulation { vector <string> triangulate(vector <int> x, vector <int> y) { int n = x.size(); vector<P> p, p2; rep(i, n) p.pb(P(x[i], y[i])), p2.pb(p[i] + p[i]); vector<L> lines, pl; rep(i, n) pl.pb(L(p[i], p[(i+1)%n])); vpii v; //greedyにやる rep(ii, n-3) { rep(j, n) reu(k, j+1, n) { L s = L(p[j], p[k]); bool b = true; int pcount = 0; rep(l, n) { int c = intersectSS(pl[l], s); b &= c != 0; if(c == 2) pcount ++; } each(l, lines) b &= intersectSS(*l, s) != 0; //多角形の内側に線分があるかどうか。 //多角形と交差していないなら、線分の適当な場所の点が多角形の内側にあるか見ればいい。 //ここではちょうど半分の点でやっている。整数でやるために多角形も2倍 b &= contains(p2, s[0] + s[1]); //始点と終点の周囲以外で触れてたら駄目 b &= pcount == 4; if(b) { lines.pb(s); v.pb(mp(j, k)); goto next; } } next:; } vs r; each(i, v) { stringstream ss; ss << i->first << " " << i->second; r.pb(ss.str()); } return r; } };