题目
给顶一个内含阻碍墙的房间,求解出一条从起点到终点的最最短路径。房间的边界 固定在 x=0,x=10,y=0 和 y=10。起点和重点固定在(0,5)和(10,5)。房间里还有 0 到 18 个 墙,每个墙有两个门。输入给定的墙的个数,每个墙的 x 位置和两个门的 y 坐标区间, 输出最短路的长度。
解题思路
虽然在这个房间里可以用很多种走法,但实际上图的节点只有墙壁的端点和始末点就行了。就比如(4,2),(4,7),(4,9),(7,4.5)(4,2),(4,7),(4,9),(7,4.5)等点,然后把这些点相连建图就可以了。我们把起点终点和每堵墙的a1,b1,a2,b2都看做图上的顶点。要完成建图,我们需要找出所有连通的路径,也就是可以直接相邻的两个顶点,完成建边。我们需要判断两点间是否没有阻碍,利用一次函数,求出连结两点的线段与每堵墙所在直线的交点,然后判断交点是否在墙洞,如果都不会被所有墙挡,那么这两个点就可以建一条边,该边的权值就是这两点之间的距离,可以通过公式求出。最后利用弗洛伊德算法求出最短路径即可。
细节说明
直线求解
就是简单的一次函数
运行结果
测试数据
输入
2
4 2 7 8 9
7 3 4.5 6 7
输出
10.06
源代码
#include <iostream>
#include <math.h>
#include <algorithm>
using namespace std;
struct wall {//封装墙的横坐标和每个断点的纵坐标
double x;
double y[5];
}w[20];
int n;//墙壁数量
double e[85][85];//邻接矩阵
//判断墙wa1上的n1点,是否可以直接与墙wa2上的n2点连接
bool canLink(int wa1, int wa2, int n1, int n2) {
if (wa2 - wa1 == 1) {//两堵墙相邻肯定可以连接
return true;
}
//求出两点所在的直线信息
double x1 = w[wa1].x, x2 = w[wa2].x;//横坐标
double y1 = w[wa1].y[n1], y2 = w[wa2].y[n2];//纵坐标
double k = (y2 - y1) / (x2 - x1);//斜率
double b = y1 - x1 * k;//截距
//判断是否可行
//遍历wa1到wa2之间的所有墙壁
for (int i = wa1 + 1; i < wa2; i++) {
double y = k * w[i].x + b;//直线经过墙y的纵坐标
//与墙i有交点,不可连接
if (y<w[i].y[1] || y>w[i].y[2] && y<w[i].y[3] || y>w[i].y[4]) {
return false;
}
}
//没有交点,可连接
return true;
}
//建边 连接墙wa1上的断点n1与墙wa2上的断点n2
void addEdge(int wa1, int wa2, int n1, int n2) {
if (!canLink(wa1, wa2, n1, n2)) {//有阻挡不可直接连接
return;
}
double x1 = w[wa1].x, x2 = w[wa2].x;//横坐标
double y1 = w[wa1].y[n1], y2 = w[wa2].y[n2];//纵坐标
double dist = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));//两点间距离
e[wa1 * 4 + n1][wa2 * 4 + n2] = dist;
e[wa2 * 4 + n2][wa1 * 4 + n1] = dist;//储存代表边的权值
}
int main() {
cout << "输入墙的数量:";
cin >> n;
//初始化矩阵e,所有边都设为不通,权值无穷大
memset(e, 127, sizeof(e));
//输入墙的信息
cout << "请输入墙的信息:" << endl;
for (int i = 1; i <= n; i++) {
cin >> w[i].x;
for (int j = 1; j <= 4; j++) {
cin >> w[i].y[j];
}
}
//初始化起点与终点
w[0].x = 0;
w[++n].x = 10;
for (int i = 1; i <= 4; i++) {
w[0].y[i] = 5;
w[n].y[i] = 5;
}
//建边
for (int i = 0; i < n; i++) {//第i堵墙
for (int j = i + 1; j <= n; j++) {//第堵墙到第j堵墙
for (int k = 1; k <= 4; k++) {//第i堵墙上的第k个结点
for (int t = 1; t <= 4; t++) {//第j堵墙上的第t个结点
addEdge(i, j, k, t);
}
}
}
}
for (int i = 0; i < 85; i++) {
e[i][i] = 0;//对角线上为0
}
//弗洛伊德算法求最短路径
for (int k = 1; k <= n * 4 + 4; k++) {
for (int i = 1; i <= n * 4 + 4; i++) {
for (int j = 1; j <= n * 4 + 4; j++) {
e[i][j] = min(e[i][j], e[i][k] + e[k][j]);
}
}
}
cout << "最短距离为:" << e[1][n * 4 + 1];
return 0;
}
声明
此题思路来源于 洛谷 P1354