USACO 3.4.1 fence4解题报告
这个题在我第一次做USACO的时候被我生生的扔掉了(交了个标程……)。我对计算几何这个东西完全没有感觉。但是这回借此机会搞一了一下,尽管用的不是这个题的完美解法(完美解法简直是BT……)。
这个题吧,有几种方法。最SB的一种方法就是,从viewer这个点引出的一条射线开始,跟每条线段做相交实验,做完之后旋转一个很小的角度。重复这两步,直到绕完一圈……这个方法虽然很SB,但是那个角度控制得当这个题还是可以过的(可见USACO的数据多么的弱啊)。还有一种相对高级点的方法,就是对于每条线,相对于其他线段放在一起看是否会被挡上。处理只当上一半的情况的时候,可以二分。直到发现我们发现这条线段有一个我们可以接受的长度是露出来的就好了。当然这个也是近似算法,相比起第一个方法就要强很多,至少精度方面有很大的进步。第三种就是完美算法,其实有两种,但是这两种里面有一种硬搞的实在是恶心到死……另一种是基于线段覆盖的,将这些线段按距离viewer的距离排序(说实话我没明白怎么排序),然后做线段覆盖……这方法因为不涉及到什么精度问题而且据说不是很难写,所以是一种很好的完美算法。
我个人而言,写的是二分那种。这程序有一半左右都是计算几何模板,可见计算几何是个多么坑爹的东西啊…………=_=|||
计算几何模板这东西果然得直接复制粘贴,自己重写的时候太容易错了!!
代码:
/* TASK:fence4 LANG:C++ */ #include <iostream> #include <fstream> #include <cmath> #include <climits> #include <cstring> #define EPS 1e-6 using namespace std; class point { public: point(){}; point(double tx, double ty):x(tx), y(ty) {} double x, y; }; class seg { public: seg(){} seg(point a, point b) {s = a; t = b;} point s, t; }; double dist(point a, point b) { return (sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y))); } double multiply(point sp, point ep, point op) { return (((sp.x - op.x) * (ep.y - op.y)) - ((sp.y - op.y) * (ep.x - op.x))); } bool checkOnline(seg l, point p) { return ((multiply(l.s, p, l.t) == 0) && (((p.x - l.s.x) * (p.x - l.t.x) <= 0) && ((p.y - l.s.y) * (p.y - l.t.y) <= 0))); } point midPoint(point a, point b) { return point((a.x + b.x) / 2, (a.y + b.y) / 2); } bool intersect(seg a, seg b) { return ((max(a.s.x, a.t.x) >= min(b.s.x, b.t.x)) && (max(b.s.x, b.t.x) >= min(a.s.x, a.t.x)) && (max(a.s.y, a.t.y) >= min(b.s.y, b.t.y)) && (max(b.s.y, b.t.y) >= min(a.s.y, a.t.y)) && (multiply(b.s, a.t, a.s) * multiply(a.t, b.t, a.s) >= 0) && (multiply(a.s, b.t, b.s) * multiply(b.t, a.t, b.s) >= 0)); } bool intersect_A(seg a, seg b) { return ((intersect(a, b)) && (!checkOnline(a, b.s)) && (!checkOnline(a, b.t)) && (!checkOnline(b, a.s)) && (!checkOnline(b, a.t))); } //---------------------以上是模板,这长度=_=||---------------------------- int n, res; point p[2001], viewer; seg s[2001]; bool vis[2001]; void init() { scanf("%d", &n); scanf("%lf%lf", &viewer.x, &viewer.y); for (int i = 0; i < n; i++) scanf("%lf%lf", &p[i].x, &p[i].y); for (int i = 0; i < n - 1; i++) s[i] = seg(p[i], p[i + 1]); s[n - 1] = seg(p[0], p[n - 1]); } bool check(int x, seg a) { if (dist(a.s, a.t) < EPS) return 0; int flag = 0; for (int i = 0; i < n; i++) if (i != x) { if (intersect(seg(viewer, a.s), s[i]) && intersect(seg(viewer, a.t), s[i])) { flag = 1; break; } if (intersect_A(seg(viewer, a.s), s[i]) || intersect_A(seg(viewer, a.t), s[i])) flag = 2; } if (flag == 0) return 1; else if (flag == 1) return 0; else return ((check(x, seg(a.s, midPoint(a.s, a.t)))) || (check(x, seg(midPoint(a.s, a.t), a.t)))); } void solve() { res = 0; memset(vis, 0 ,sizeof(vis)); for (int i = 0; i < n; i++) if (check(i, s[i])) { res++; vis[i] = 1; } } void print() { printf("%d\n", res); for (int i = 0; i < n - 2; i++) if (vis[i]) printf("%d %d %d %d\n", (int)s[i].s.x, (int)s[i].s.y, (int)s[i].t.x, (int)s[i].t.y); if (vis[n - 1]) printf("%d %d %d %d\n", (int)s[n - 1].s.x, (int)s[n - 1].s.y, (int)s[n - 1].t.x, (int)s[n - 1].t.y); if (vis[n - 2]) printf("%d %d %d %d\n", (int)s[n - 2].s.x, (int)s[n - 2].s.y, (int)s[n - 2].t.x, (int)s[n - 2].t.y); } int main() { freopen("fence4.in", "r", stdin); freopen("fence4.out", "w", stdout); init(); solve(); print(); return 0; }