1 条题解
-
0
问题分析
题目要求在正方形 内给定 个点,通过连接这些点和正方形的四个角,找到一种连接方式使得图的总长度最短。同时,需要调整这些点的位置,使得与其他点的相对位置关系保持不变的情况下,移动的总距离最小。最终输出这个最小的移动距离总和。
核心思路
-
预定义的关键点和线段
题目中预定义了两个关键区域,每个区域有 5 条线段和两个特殊点:- 区域 1:点
one
和two
,线段Line1, Line2, Line3, Line4, Line5
。 - 区域 2:点
three
和four
,线段Line6, Line7, Line8, Line9, Line10
。
这些线段和点的位置是根据几何特性设计的,目的是模拟一种“最优连接方式”,让每个点尽量沿最近的线段连接到正方形的顶点上。
- 区域 1:点
-
计算点到线段的最小距离
对于每个点,计算它到某个区域所有线段的最短距离。如果点投影到线段上,就用投影点计算距离;如果投影点超出了线段范围,则取到线段起点或终点的距离。 -
寻找最优连接点对
遍历所有点对,计算每个点对分别连接到两个关键点的总长度(考虑点到关键点的距离和垂直距离)。选择一组点对,使得这些距离的差值最小。 -
计算总移动距离
对于每个区域,选择最优的点对后,计算所有点的总移动距离,即每个点到最近线段的距离之和。 -
输出结果
比较两个区域的最小移动距离,输出较小值。
代码细节解析
#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<cmath> #include<algorithm> #define esp 1e-6 // 定义一个小的误差范围,用于比较浮点数 using namespace std; int n; // 点的数量 struct Point { double x, y; // 点的坐标 Point() {} // 默认构造函数 Point(double _x, double _y) { x = _x; y = _y; } // 带参数的构造函数 // 重载运算符 '-',用于计算两个点的向量差 Point operator - (const Point& b) const { return Point(x - b.x, y - b.y); } // 重载运算符 '*',用于计算两个向量的点积 double operator * (const Point& b) const { return x * b.x + y * b.y; } } p[110]; // 存储所有点的数组 struct Line { Point s, e; // 线段的起点和终点 Line(Point p1, Point p2) { // 构造函数 s = p1; e = p2; } }; double best = 1e9; // 用于存储全局最小值,初始化为一个很大的数 // 预定义的关键点和线段 const Point only(0.5, 0.5); // 正方形的中心点 const Point one(sqrt(3.0) / 6.0, 0.5); // 区域1的关键点1 const Point two(1.0 - sqrt(3.0) / 6.0, 0.5); // 区域1的关键点2 const Point three(0.5, sqrt(3.0) / 6.0); // 区域2的关键点1 const Point four(0.5, 1.0 - sqrt(3.0) / 6.0); // 区域2的关键点2 const Point lu(0, 1), ld(0, 0), ru(1, 1), rd(1, 0); // 正方形的四个顶点 // 预定义的线段 const Line Line1(one, two); const Line Line2(lu, one); const Line Line3(ru, two); const Line Line4(ld, one); const Line Line5(rd, two); const Line Line6(three, four); const Line Line7(rd, three); const Line Line8(lu, four); const Line Line9(ld, three); const Line Line10(ru, four); // 将线段分组,分别对应两个区域 Line line1[5] = {Line1, Line2, Line3, Line4, Line5}; Line line2[5] = {Line6, Line7, Line8, Line9, Line10}; // 计算两点之间的欧几里得距离 double dist(Point n1, Point n2) { return sqrt((n1.x - n2.x) * (n1.x - n2.x) + (n1.y - n2.y) * (n1.y - n2.y)); } // 计算点 P 到线段 L 的最小距离 double Get_nearest_point(Point P, Line L) { Point result; // 用于存储最近点 // 计算点 P 在线段 L 上的投影位置 double t = ((P - L.s) * (L.e - L.s)) / ((L.e - L.s) * (L.e - L.s)); if (t >= 0.0 && t <= 1.0) { // 如果投影点在线段范围内,计算投影点的坐标 result.x = L.s.x + (L.e.x - L.s.x) * t; result.y = L.s.y + (L.e.y - L.s.y) * t; } else { // 如果投影点超出线段范围,选择离点 P 更近的端点 if (dist(P, L.s) - dist(P, L.e) < esp) result = L.s; else result = L.e; } return dist(result, P); // 返回点 P 到最近点的距离 } // 计算点 p[x] 到一组线段 l 的最小距离 double Get_dist_to_line(int x, Line l[5]) { double _Min = 1e9; // 初始化最小距离为一个很大的数 for (int i = 0; i < 5; i++) { _Min = min(_Min, Get_nearest_point(p[x], l[i])); // 遍历所有线段,找到最小距离 } return _Min; } // 解决函数,计算在给定线段和关键点的情况下,所有点的最小移动距离 double solve(Line l[5], Point p1, Point p2) { int pos1, pos2; // 用于存储最优的点对 double ans = 0, _Min = 1e9; // 初始化最小值和最终答案 // 遍历所有点对,寻找最优连接点对 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j) continue; // 跳过相同的点 double d1 = dist(p[i], p1), d2 = dist(p[j], p2); // 计算点到关键点的距离 double d3 = Get_dist_to_line(i, l), d4 = Get_dist_to_line(j, l); // 计算点到线段的最小距离 // 更新最小值和最优点对 if (_Min > d1 - d3 + d2 - d4) { _Min = d1 - d3 + d2 - d4; pos1 = i; pos2 = j; } } } // 计算最优点对的总距离 ans = dist(p[pos1], p1) + dist(p[pos2], p2); // 计算其他点的移动距离 for (int i = 0; i < n; i++) { if (i == pos1 || i == pos2) continue; // 跳过最优点对 ans += Get_dist_to_line(i, l); // 累加其他点到线段的最小距离 } return ans; // 返回总移动距离 } int main() { while (~scanf("%d", &n) && n) { // 读取点的数量,如果为0则结束 best = 1e9; // 初始化全局最小值 for (int i = 0; i < n; i++) { scanf("%lf%lf", &p[i].x, &p[i].y); // 读取每个点的坐标 } if (n == 1) { // 如果只有一个点,直接计算该点到中心点的距离 printf("%.3lf\n", dist(p[0], only)); continue; } // 分别计算两个区域的最小移动距离,并输出较小值 printf("%.3lf\n", min(solve(line1, one, two), solve(line2, three, four)) + esp); } return 0; }
-
Point
和Line
结构体Point
结构体用于表示点,重载了运算符-
和*
,分别用于计算向量减法和点积。Line
结构体用于表示线段,由起点s
和终点e
组成。
-
关键点和线段的定义
- 定义了两个区域的关键点和线段,这些点和线段是根据几何特性预设的,用于计算点到线段的最小距离。
-
dist
函数
用于计算两点之间的欧几里得距离。 -
Get_nearest_point
函数
用于计算点到线段的最小距离。根据向量投影计算点在直线上的投影位置,如果投影位置在线段范围内,则计算投影点到点的距离;否则,计算点到线段起点或终点的距离。 -
Get_dist_to_line
函数
遍历某个区域的所有线段,找到点到这些线段的最小距离。 -
solve
函数- 对于每个区域,找到一组最优的点对,使得连接到关键点的总长度最小。
- 遍历所有点对,计算每个点对到关键点的距离,减去到最近线段的距离,选择最小值。
- 计算所有点的总移动距离,包括最优点对到关键点的距离和其他点到最近线段的距离。
-
主函数逻辑
- 读取输入,处理每个测试用例。
- 如果只有一个点,直接输出该点到中心点
only
的距离。 - 对于多个点,分别计算两个区域的最小移动距离,输出较小值。
总结
本题通过预设的关键点和线段,模拟了一种最优连接方式。通过计算点到线段的最小距离,选择最优的点对,最终实现最小化移动距离的目标。代码整体逻辑清晰,难点在于理解几何关系和线段的预设设计。
-
- 1
信息
- ID
- 1054
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 7
- 已通过
- 1
- 上传者