POJ1066 Treasure Hunt

题目来源:http://poj.org/problem?id=1066

题目大意:

  一个正方形迷宫里的某一个点处藏有宝藏,但是这个迷宫除了边界四周有强外,内部还有一些交错的内墙,把迷宫分隔成小房间。如图所示。求问要从迷宫外通至宝藏处最少要穿过多少堵墙,而且穿墙的要求是:只能在房间边界的中点处砸开一扇门。

    显然图示的例子需要穿过两堵墙。

输入:只含一个用例。第一行整数n(0<=n<=30)表示内墙数,接下来n行每行的4个整数为墙起点和终点的坐标: x1 y1 x2 y2. 迷宫四个顶点的坐标分布是(0, 0) (0, 100) (100, 0) (100, 100)。内墙的起点和终点均不在正方形的顶点上。内墙总是从正方形的四条边(外墙)中的一条出发到达另一条。不存在三条以上墙相交与一点的情况。墙不存在重合的情况。最后一行两个浮点数表示宝藏的坐标。宝藏不位于任何一堵墙上。

输出:Number of doors = k, 其中k为求得的最少墙数。


Sample Input

7 
20 0 37 100 
40 0 76 100 
85 0 0 75 
100 90 0 90 
0 71 100 61 
0 14 100 38 
100 47 47 100 
54.5 55.4 

Sample Output

Number of doors = 2

  本题比较直接的思路是以所有线段终点以及宝藏点为顶点,可达关系为边建立一个图,然后求宝藏点到各位于外墙上的点处的最短路径,然后选出最小的一个。不过这种方法比较耗时,实现起来也麻烦一些,有一种更简单的方法:

  先还是要求出外墙上每一段墙的中点,然后通过把中点与宝藏点连起来,形成一条线段,求线段与内墙交点的最少数目,求得最小值后加1即可得所需答案。

 在求外墙中点时可以先对所有内墙顶点包括四边形顶点按极角排序。极角的相关背景:可以参考这里

 然后依次求各中点与所有内墙的相交点个数,选最小值。所以问题最终化为“判断两条线段是否相交”。我的做法是利用中学数学里的线段参数方程。

线段pq上的点满足参数方程:x = xp + t*(xq - xp); y = yp + t*(yq - yp). 其中 t 在[0,1]内。

若线段 p1q1 与线段 p2q2 相交,则方程组:

xp1 + t * (xq1 - xp1) = xp2 + r * (xq2 - xp2);

yp1 + t * (yq1 - yp1) = yp2 + r * (yq2 - yp2);

求出其中的 t 如果满足 t >= 0 && t <= 1, 则说明线段相交。最傻的办法了吧,不知道还有其它简单的方法吗?

 1 //////////////////////////////////////////////////////////////
 2 //        POJ1066 Treasure Hunt
 3 //        Memory: 744K        Time: 110MS
 4 //        Language: G++        Result : Accepted
 5 //////////////////////////////////////////////////////////////
 6 
 7 #include <iostream>
 8 #include <algorithm>
 9 
10 using namespace std;
11 
12 struct  Point {
13     double x, y;
14 } points[65];
15 
16 struct Line {
17     Point p, q;
18 } lines[30];
19 
20 int p_cnt, l_cnt, min_p;
21 double eps = 1e-8, t_x, t_y;
22 
23 bool cmp(const Point &a, const Point &b) {
24     //极角比较, 极角相等时,按距离排
25     long long xmult = a.x * b.y - b.x * a.y;
26     if (xmult == 0) {
27         if (a.y == 0 && b.y == 0) {
28             return a.x < b.x;
29         } else if (a.x == 100 && b.x == 100) {
30             return a.y < b.y;
31         } else if (a.y == 100 && b.y == 100) {
32             return a.x > b.x;
33         } else {
34             return a.y > b.y;
35         }
36     } else {
37         return xmult > 0;
38     }
39 }
40 
41 bool intersect(Line &l1, Line &l2) {
42     //利用线段的参数方程求解
43     double xp1 = l1.p.x, xp2 = l2.p.x, xq1 = l1.q.x, xq2 = l2.q.x;
44     double yp1 = l1.p.y, yp2 = l2.p.y, yq1 = l1.q.y, yq2 = l2.q.y;
45     double denominator = (yq2 - yp2) * (xq1 - xp1) - (yq1 - yp1) * (xq2 - xp2);
46     if (denominator < eps && denominator > -1 * eps) {
47         return false;    //分母为0,平行
48     }
49     double t = ((yp1 - yp2) * (xq2 - xp2) + (yp2 - yq2) * (xp1 - xp2)) / denominator;
50     if (t >= eps && t <= 1) { //参数t在0到1之间,说明线段相交
51         return true;
52     }
53     return false;
54 }
55 
56 int main(void) {
57     cin >> l_cnt;
58     p_cnt = 2 * l_cnt;
59     min_p = l_cnt;
60     for (int i = 0; i < l_cnt; ++i) {
61         cin >> points[2 * i].x >> points[2 * i].y;
62         lines[i].p = points[2 * i];
63         cin >> points[2 * i + 1].x >> points[2 * i + 1].y;
64         lines[i].q = points[2 * i + 1];
65     }
66     p_cnt += 4;
67     //将顶点坐标加入
68     points[p_cnt - 1] = { 0, 0 };
69     points[p_cnt - 2] = { 0, 100 };
70     points[p_cnt - 3] = { 100, 0 };
71     points[p_cnt - 4] = { 100, 100 };
72 
73     cin >> t_x >> t_y;
74     sort(points, points + p_cnt, cmp);
75     points[p_cnt] = { 0, 0 };
76     for (int i = 0; i < p_cnt; ++i) {
77         //中点
78         points[i].x = (points[i].x + points[i + 1].x) / 2;
79         points[i].y = (points[i].y + points[i + 1].y) / 2;
80         Line l = { points[i], { t_x, t_y } };
81         int i_cnt = 0;
82         for (int j = 0; j < l_cnt; ++j) {
83             if (intersect(l, lines[j])) {
84                 ++i_cnt;
85             }
86         }
87         min_p = min_p > i_cnt ? i_cnt : min_p;
88     }
89     cout << "Number of doors = " << min_p + 1 << endl;
90     return 0;
91 }
View Code

此外用C++提交一直Compile Error,G++ AC, 还没想明白哪里的问题. =。=...

posted @ 2013-12-10 15:23  小菜刷题史  阅读(258)  评论(0编辑  收藏  举报