• 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, 还没想明白哪里的问题. =。=...

  • 相关阅读:
    CodeForces 452C Magic Trick (排列组合)
    zoj 3209 Treasure Map(精确覆盖)
    POJ 1459 Power Network(网络流 最大流 多起点,多汇点)
    POJ 1273 Drainage Ditches(网络流 最大流)
    HDU Tickets(简单的dp递推)
    ZOJ 3080 ChiBi(spfa)
    URAL 1036(dp+高精度)
    最佳的 清楚浮动 clearfix
    响应式开发
    javascript 性能优化
  • 原文地址:https://www.cnblogs.com/dengeven/p/3448611.html
Copyright © 2020-2023  润新知