合工大 程序设计艺术 实验三 房间最短路径

题目

给顶一个内含阻碍墙的房间,求解出一条从起点到终点的最最短路径。房间的边界 固定在 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桃气十足

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值