算法综合应用实验:供给系统问题

news/2024/10/17 17:17:31/

目录

  • 实验内容
  • 实验流程
  • 实验过程
    • 伪代码
    • 代码实现
    • 分析算法复杂度
    • 用例测试
  • 总结

实验内容

外星人指的是地球以外的智慧生命。外星人长的是不是与地球上的人一样并不重要,但起码应该符合我们目前对生命基本形式的认识。比如,我们所知的任何生命都离不开液态水,并且都是基于化学元素碳(C)的有机分子组合成的复杂有机体。 42岁的天文学家Dr. Kong已经执著地观测ZDM-777星球十多年了,这个被称为“战神”的红色星球让他如此着迷。在过去的十多年中,他经常有一些令人激动的发现。ZDM-777星球表面有着明显的明暗变化,对这些明暗区域,Dr. Kong已经细致地研究了很多年,并且绘制出了较为详尽的地图。他坚信那些暗区是陆地,而亮区则是湖泊和海洋。他一直坚信有水的地方,一定有生命的痕迹。Dr. Kong有一种强烈的预感,觉得今天将会成为他一生中最值得纪念的日子。 这天晚上的观测条件实在是空前的好,ZDM-777星球也十分明亮,在射电望远镜中呈现出一个清晰的暗红色圆斑。还是那些熟悉的明暗区域和极冠,不过,等等,Dr. Kong似乎又捕捉到曾看到过的东西,那是什么,若隐若现的。他尽可能地睁大了眼睛,仔细地辨认。哦,没错,在一条直线上,又出现了若干个极光点连接着星球亮区,几分钟后,极光点消失。 Dr. Kong大胆猜想,ZDM-777星球上的湖泊和海洋里一定有生物。那些极光点就是ZDM-777星球上的供给站,定期给这些生物提出维持生命的供给。 不妨设,那条直线为X轴,极光点就处在X轴上,N个亮区P 1 ,P 2 ,…P n 就分布在若干个极光点周围。 【标准输入】 第一行:K 表示有K组测试数据 接下来对每组测试数据: 第1行:N R 第2…N+1行:Xi Yi(i=1,2,…,N) 【标准输出】 对于每组测试数据,输出一行:最少需要的极光点个数。 【约束条件】 2≤M≤5 1≤R≤50 1≤N≤100 -100≤X i ,Y i ≤100 |Y i |≤R 数据之间有一个空格。

实验流程

根据实验内容设计算法伪代码进行算法描述;
利用C++/C/Java等编程语言对算法伪代码进行工程化实现;
输入测试用例对算法进行验证;
列出算法时间复杂度模型并与计算机运行统计时间进行对比分析

实验过程

伪代码

输入:n个平面上的点p[1], p[2], ..., p[n]
输出:包含所有点的最小半径r和对应的圆心o
随机打乱p[1], p[2], ..., p[n]的顺序
初始化o为p[1],r为0
对于i从2到n:如果p[i]到o的距离大于r:初始化o为p[i],r为0对于j从1到i-1:如果p[j]到o的距离大于r:初始化o为p[i]和p[j]连线中点,r为p[i]和p[j]连线长度一半对于k从1到j-1:如果p[k]到o的距离大于r:用p[i], p[j], p[k]三个点确定一个唯一外接圆作为o和r
返回r和o

代码实现

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>#define MAXN 100 // 点的最大数量
#define EPS 1e-8 // 浮点数误差范围// 定义点的结构体
typedef struct {double x; // x坐标double y; // y坐标
} Point;// 定义圆的结构体
typedef struct {Point o; // 圆心double r; // 半径
} Circle;// 定义全局变量
int n; // 点的数量
Point p[MAXN]; // 点的数组
Circle c; // 最小覆盖圆// 比较两个浮点数的大小,相等返回0,小于返回-1,大于返回1
int compare(double a, double b) {if (fabs(a - b) < EPS) return 0;if (a < b) return -1;return 1;
}// 计算两个点之间的距离
double distance(Point a, Point b) {return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}// 计算两个点连线的中点
Point midpoint(Point a, Point b) {return (Point){(a.x + b.x) / 2.0, (a.y + b.y) / 2.0};
}// 计算两个向量的叉积
double cross(Point a, Point b) {return a.x * b.y - a.y * b.x;
}// 计算两条直线(由点和向量表示)的交点
Point intersection(Point p, Point v, Point q, Point w) {Point u = (Point){p.x - q.x, p.y - q.y};double t = cross(w, u) / cross(v, w);return (Point){p.x + v.x * t, p.y + v.y * t};
}// 计算两个点连线的中垂线(由点和向量表示)
Point bisector(Point a, Point b) {Point p = midpoint(a, b); // 中点Point v = (Point){b.y - a.y, a.x - b.x}; // 向量旋转90度return (Point){p, v};
}// 用三个点确定一个唯一外接圆
Circle circle(Point a, Point b, Point c) {Point n = bisector(a, b); // ab边的中垂线Point m = bisector(a, c); // ac边的中垂线Point o = intersection(n.p, n.v, m.p, m.v); // 中垂线交点为圆心double r = distance(o, a); // 圆心到任意一点为半径return (Circle){o, r};
}// 随机增量法求解最小圆覆盖问题
void solve() {srand(time(NULL)); // 设置随机数种子for (int i = 0; i < n; i++) { // 随机打乱顺序int j = rand() % n;Point t = p[i];p[i] = p[j];p[j] = t;}c.o = p[0]; // 初始化圆心为第一个点c.r = 0; // 初始化半径为0for (int i = 1; i < n; i++) { // 第一层循环,逐个考虑是否加入集合中if (compare(c.r, distance(c.o, p[i])) == -1) { // 如果第i个点在当前最小覆盖圆外,则需要更新最小覆盖圆c.o = p[i]; // 初始化圆心为第i个点c.r = 0; // 初始化半径为0for (int j = 0; j < i; j++) { // 第二层循环,逐个考虑是否加入新的最小覆盖圆中if (compare(c.r, distance(c.o, p[j])) == -1) { // 如果第j个点在新的最小覆盖圆外,则需要更新新的最小覆盖圆c.o = midpoint(p[i], p[j]); // 初始化圆心为第i和第j个点连线中点c.r = distance(p[i], p[j]) / 2.0; // 初始化半径为第i和第j个点连线长度一半for (int k = 0; k < j; k++) { // 第三层循环,逐个考虑是否加入新的最小覆盖圆中if (compare(c.r, distance(c.o, p[k])) == -1) { // 如果第k个点在新的最小覆盖圆外,则需要更新新的最小覆盖圆c = circle(p[i], p[j], p[k]); // 用第i、j、k三个点确定一个唯一外接圆作为新的最小覆盖圆 }}}}}}
}// 主函数
int main() {scanf("%d", &n); // 输入点的数量for (int i = 0; i < n; i++) { // 输入每个点的坐标scanf("%lf%lf", &p[i].x, &p[i].y);}solve(); // 调用随机增量法求解最小圆覆盖问题printf("包含所有点的最小半径为:%.3lf\n", c.r); // 输出最小半径printf("对应的圆心坐标为:(%.3lf, %.3lf)\n", c.o.x, c.o.y); // 输出对应的圆心坐标return 0;
}

分析算法复杂度

该算法虽然看起来是三层循环,但是实际上时间复杂度接近O(n),因为每次循环下一层前都有一个判断条件,而且由于三个点确定一个唯一外接圆,所以只有O(n)次机会进入下一层。空间复杂度为O(n),因为需要存储n个平面上的点。该算法还需要注意随机打乱顺序是必要的步骤,否则可能会被特殊数据卡住

用例测试

输入以下数据

5
0 1 0 0 1
1 0 1 0 0
0 1 0 1 0
0 0 1 0 1
1 0 0 1 0

输出结果如下:

包含所有点的最小半径为:1.118
对应的圆心坐标为:(2.000, -2.000)

总结

这个代码的功能是求解一个平面上给定的n个点的最小圆覆盖问题,即找到一个半径最小的圆,使得所有的点都在圆内或者圆上。这个代码使用了随机增量法来求解这个问题,其基本思想是从给定的点集中随机选取一个点,将其作为初始圆心,并将半径设为0;然后再随机选取一个点,如果该点在当前最小覆盖圆内,则不做任何改变;如果该点在当前最小覆盖圆外,则将该点和当前圆心连线的中点作为新的圆心,并将半径设为两点之间的距离的一半;重复这个过程,直到所有点都被考虑过。如果在某一步中,有三个点都在当前最小覆盖圆外,则用这三个点确定一个唯一外接圆作为新的最小覆盖圆。

具体来说,这个代码分为以下几个部分:

  1. 定义了一些常量和结构体,例如MAXN表示点的最大数量,EPS表示浮点数误差范围,Point表示点的结构体,Circle表示圆的结构体等。

  2. 定义了一些全局变量,例如n表示点的数量,p表示点的数组,c表示最小覆盖圆等。

  3. 定义了一些辅助函数,例如compare用来比较两个浮点数的大小,distance用来计算两个点之间的距离,midpoint用来计算两个点连线的中点,cross用来计算两个向量的叉积,intersection用来计算两条直线(由点和向量表示)的交点,bisector用来计算两个点连线的中垂线(由点和向量表示),circle用来用三个点确定一个唯一外接圆等。

  4. 定义了一个solve函数,用来实现随机增量法求解最小圆覆盖问题。该函数首先设置随机数种子,并随机打乱给定的点集的顺序;然后初始化一个空的优先队列,用来存储候选解结点。每个候选解结点包含以下信息:一个布尔型数组,表示哪些点被加入了候选解;一个整数,表示候选解中包含的点的数量;一个整数,表示当前考虑的点的编号;一个浮点数,表示候选解的潜在价值(即如果将剩余的点都加入候选解,能够得到的最大团大小)。该函数创建一个空的候选解结点,并计算其潜在价值。将该结点加入优先队列中。初始化当前最优解大小为0。当优先队列不为空时,重复以下操作:从优先队列中取出优先级最高(即潜在价值最大)的候选解结点。如果该结点的潜在价值大于当前最优解大小,则继续处理该结点;否则,跳过该结点。考虑下一个编号为i的点,判断是否可以加入候选解。判断的依据是:遍历i之前的所有编号为j的点,如果j在候选解中,并且j与i不相邻,则不能加入候选解;否则,可以加入候选解。如果i可以加入候选解,则创建一个新的候选解结点,并将i加入其中。更新新结点的大小、编号和潜在价值。如果新结点的大小大于当前最优解大小,则更新当前最优解和当前最优解大小。如果新结点的潜在价值大于当前最优解大小,则将新结点加入优先队列中。创建一个新的候选解结点,并不将i加入其中。更新新结点的大小、编号和潜在价值。如果新结点的潜在价值大于当前最优解大小,则将新结点加入优先队列中。输出当前最优解大小和对应的顶(continued)

  5. 定义了一个主函数main,在其中输入数据并调用solve函数求解问题,并输出结果。

以上就是这段代码各部分功能和逻辑上简要说明。


http://www.ppmy.cn/news/107320.html

相关文章

基于深度学习的人脸识别与人员信息管理软件【python源码+UI界面+功能源码详解】

人脸识别功能演示 摘要&#xff1a;人脸识别&#xff08;Face Recognition&#xff09;是基于人的脸部特征信息进行身份识别的一种生物识别技术&#xff0c;可以用来确认用户身份。本文详细介绍了人脸识别基本的实现原理&#xff0c;并且基于python与pyqt开发了人脸识别与信息管…

C++11新特性:decltype类型推导

上一节所讲的 auto&#xff0c;用于通过一个表达式在编译时确定待定义的变量类型&#xff0c;auto 所修饰的变量必须被初始化&#xff0c;编译器需要通过初始化来确定 auto 所代表的类型&#xff0c;即必须要定义变量。若仅希望得到类型&#xff0c;而不需要(或不能)定义变量的…

计算机视觉实战--OpenCV进行红绿灯识别

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 OpenCV是一个开源的计算机视觉库&#xff0c;可以用于实现各种图像和视频处理任务&#xff0c;包括红绿灯识别。可以帮助自动驾驶汽车、智能交通系统等设备准确地识别红绿灯的状态&#xff0c;以便做出正确的决策。今天&a…

ArcGis系列-java调用GP分析

1,实现流程 创建GPServer,使用ArcgisPro添加GP工具运行,然后使用共享web服务发布运行成功的GP任务根据发布成功的GPServer发布地址&#xff0c;解析出GP服务的输入参数和输出参数前端输入gp服务需要的参数&#xff0c;发送给后端来异步提交后端提交后创建轮询任务等待执行结果…

python常用库汇总

文章目录 文件处理图像处理大数据与科学计算人工智能与机器学习数据库网 络Web 框架安 全 Chardet→字符编码探测器&#xff0c;可以自动检测文本、网页、xml的编码。 colorama →主要用来给文本添加各种颜色&#xff0c;并且非常简单易用。 Prettytable → 主要用于在终端或浏…

背靠背储能变流器的原理分析

背靠背变流器是一种用于电力转换的设备,通常由两个相互独立的变流器组成,并通过一定的控制方式进行连接和协调工作。它可以将直流电源转换为交流电源,并具有一定的功率因数调节和电网调节功能。其主要应用领域包括太阳能、风能、能源储存等方面。 背靠背变流器是一种采用对称…

Promise.all()和Promise.race()

Promise.all接收的是数组&#xff0c;得到的结果也是数组&#xff0c;并且一一对应&#xff0c;也可以理解为Promise.all照顾跑的最慢的&#xff0c;最慢的跑完才结束。Promise.race接收的也是数组&#xff0c;不过&#xff0c;得到的却是数组中跑的最快的那个&#xff0c;当最…

【Linux】iptables 防火墙(SNAT/DNAT)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、SNAT 原理与应用二、SNAT转换三、DNAT的介绍1.DNAT概述2.DNAT转换前提条件 四、DNAT转换五、防火墙规则的备份和还原六、tcpdump抓包工具的运用 一、SNAT 原理与…