poj1556

题目链接

题意:在平面直角坐标系上有一个10*10的正方形房间,房间中有n堵平行y方向的墙,每堵墙上有两扇门,

问从房间最左边的(0,5)处,通过门,到达房间最右边的(10,5)处的最短距离

思路:构造一个图,对图求最短距。

构图:每扇门由两个端点构成,求出每堵墙上的门的端点到其他墙上的门的端点的距离(前提:这两个点能够直接相连不撞墙),当然,起点终点也要参与上述的求距离构图过程。

           之后,再对构好的图求最短距即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

double g[100][100], dist[100];
bool visit[100];
int n, t_id; //n为墙数,t_id为终点在图中的编号

const double eps = 1e-8;
const double inf = 999999999;

/*door[i][j][k]表示第i堵墙的j扇门的第k端点
  s为起点,t为终点
*/
struct Point {
    double x, y;
} door[20][2][2], s, t;

//叉积
double multi(Point p1, Point p2, Point p0) {
    return (p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x);
}

//判断线段ab与直线cd是否相交,过端点也算相交
bool cross(Point a, Point b, Point c, Point d) {
    if (multi(a, d, c) * multi(b, d, c) > eps) return false;
    return true;
}

//求两点距离
double dis(Point a, Point b) {
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

//判断两点是否能够直接相连不撞墙
bool judge(int i, int j, Point a, Point b) {
     for (; i <= j; i++)
         if (cross(door[i][0][0], door[i][0][1], a, b) == false && cross(door[i][1][0], door[i][1][1], a, b) == false)
            return false;
     return true;
}

//构图:将不同墙上的门之间的距离构造进图
void make_map() {
     int i, j, k, l, p, q, x, y;

     for (i = 0; i < n-1; i++)
         for (j = 0; j < 2; j++)
            for (k = 0; k < 2; k++) {
                x = 4*i+2*j+k+1;
                for (l = i + 1; l < n; l++)
                    for (p = 0; p < 2; p++)
                        for (q = 0; q < 2; q++) {
                            y = 4*l+2*p+q+1;
                            if (judge(i+1, l-1, door[i][j][k], door[l][p][q]))
                                g[x][y] = dis(door[i][j][k], door[l][p][q]);
                        }
            }
}

//求最短距
double Dikstra() {
    int i, j, cur;
    double tmp;

    memset(visit, false, sizeof (visit));
    for (i = 1; i <= t_id; i++) dist[i] = inf;
    dist[0] = 0;
    for (i = 0; i <= t_id; i++) {
        tmp = inf;
        cur = -1;
        for (j = 0; j <= t_id; j++)
            if (!visit[j] && dist[j] < tmp) {
                tmp = dist[j];
                cur = j;
            }
        visit[cur] = true;
        if (cur == t_id) break;
        for (j = 0; j <= t_id; j++) {
            if (!visit[j] && g[cur][j] > eps && dist[cur] + g[cur][j] + eps < dist[j])
                dist[j] = dist[cur] + g[cur][j];
        }
    }
    return dist[t_id];
}

int main()
{
    double x, y1, y2, y3, y4;
    int i, j, k;

    //初始化起点终点坐标
    s.x = 0; s.y = 5;
    t.x = 10; t.y = 5;
    while (scanf ("%d", &n) && n != -1) {
        for (i = 0; i < n; i++) {
            scanf ("%lf%lf%lf%lf%lf", &x, &y1, &y2, &y3, &y4);
            door[i][0][0].x = door[i][0][1].x = door[i][1][0].x = door[i][1][1].x = x;
            door[i][0][0].y = y1; door[i][0][1].y = y2;
            door[i][1][0].y = y3; door[i][1][1].y = y4;
        }
        memset(g, 0, sizeof (g));
        t_id = 4 * n + 1; //求出终点在图中的编号
        if (judge(0, n-1, s, t)) { //r若起点终点可以直接相连,则这两点距离即为答案
            printf ("%.2lf\n", dis(s, t));
            continue;
        }
        int id;
        //将起点和终点与中间的墙上的门的距离构造进图
        for (i = 0; i < n; i++) {
            for (j = 0; j < 2; j++) {
                for (k = 0; k < 2; k++) {
                    id = 4*i+2*j+k+1; //求出门上的点在图中的编号
                    if (judge(0, i-1, s, door[i][j][k])) g[0][id] = dis(s, door[i][j][k]);
                    if (judge(i+1, n-1, door[i][j][k], t)) g[id][t_id] = dis(door[i][j][k], t);
                }
            }
        }
        make_map();
        printf ("%.2lf\n", Dikstra());
    }
    return 0;
}


 

你可能感兴趣的:(c)