IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 题解——7-4 圈点 -> 正文阅读

[数据结构与算法]题解——7-4 圈点

题目描述:

????????平面上有若干点,任何两个点之间的距离都不会小于0.0001,也不会大于2.0。如果我们用一个半径为1的圆来圈这些点,最多能圈住几个点。

????????注意:如果一个点在圆上,也视为被圈住了。同样,如果点和圆心之间的距离小于1.0001,也视为被圈住了。

输入格式:

????????首先输入一个整数T,表示总共有T组测试数据。 对于每组测试数据,第一行是一个整数N,表示有N个点,1<=N<=300。

输出格式:

????????对于每组测试数据,在一行上输出计算结果。

输入样例:

4
3
6.47634 7.69628
5.16828 4.79915
6.69533 6.20378
6
7.15296 4.08328
6.50827 2.69466
5.91219 3.86661
5.29853 4.16097
6.10838 3.46039
6.34060 2.41599
8
7.90650 4.01746
4.10998 4.18354
4.67289 4.01887
6.33885 4.28388
4.98106 3.82728
5.12379 5.16473
7.84664 4.67693
4.02776 3.87990
20
6.65128 5.47490
6.42743 6.26189
6.35864 4.61611
6.59020 4.54228
4.43967 5.70059
4.38226 5.70536
5.50755 6.18163
7.41971 6.13668
6.71936 3.04496
5.61832 4.23857
5.99424 4.29328
5.60961 4.32998
6.82242 5.79683
5.44693 3.82724
6.70906 3.65736
7.89087 5.68000
6.23300 4.59530
5.92401 4.92329
6.24168 3.81389
6.22671 3.62210

输出样例:

2
5
5
11

解题思路:

????????这道题的意思就是在一个没有界限的范围内给定n个点的坐标,需要你找到一个半径为1的圆来囊括尽量多的点。乍一看上去是不是和贪心算法中经典的雷达问题十分相像,只是把给定的海岸线撤除了而已,但其实不然,看上去只是擦除了一条线,但难度翻了可是不止一倍啊!我们先用这个方法来试一下:

????????雷达问题的解法就是以各个岛屿所在的位置画一个半径等同于雷达搜索半径的圆,然后在海岸线上搜索被最多圆覆盖的区间,来确定合适的位置安装雷达使数量最少。在本题中我们同样可以这样设想,以给定的点为圆心画半径为1的圆,只要我们找到一个被最多圆覆盖的区间,并计算出这个区间被多少个圆覆盖,输出覆盖在这个区间上的圆的个数即可。

????????下面我们来分析一下这个方法的缺点:

  • 第一,这个题目没有给定的范围,即题图是一个无界图,这也就给我们程序模拟造成了巨大的困难。
  • 第二,如果我们成功的建立出了模拟图,但寻找那个被最多圆覆盖的区间同样是一个问题,程序来寻找这个区间所用的算法是相当麻烦的(我是没能建起来)
  • 第三,就算找到这个区间了,再去确定有多少个圆覆盖了这个地方同样是个问题

? ? ? ? 综上,这道题用雷达问题的方法做是非常不理智的行为,希望兄弟们没有和我一样傻傻的在这个坑里打转。

? ? ? ? 正确的(目前我能想到最好的)做法应该是完全使用枚举。枚举所有的点,每两个点可以确定一个圆【因为圆的半径已知】,(两点间距超过圆半径的就确定不了一个圆,跳过这组即可)在这一个圆中保留一个内部点数最多的,加上确定圆的那两个点输出点数即可。若都确定不了一个圆或者测试数据只有一个点,则输出1。

? ? ? ? 当然啦,只要是上过学的人都知道,只有两点间距刚好等于圆的直径才能唯一的确定一个圆,其他情况都会有两个圆,这时候我们选取涵盖最多点的一个就好了。

? ? ? ? 现在我们来看一下确定圆心的方法。由数学可知,已知两点坐标和圆的半径并将其带入方程就可以知道圆心,而编程是不能列方程的,所以我们来推导一下圆心坐标和两点坐标的关系。???????????????????????????????????

? ? ? ? 推导出这样的一个方程我们就可以写代码了。话不多说,上代码:

#include<stdio.h>
#include<math.h>

#define R 1.0

struct Point{
    double x, y;
};//结构体用来储存点的坐标

double dist(double x1, double y1, double x2, double y2);//一个用来求两点间距离的函数

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int i, j, N, ans=1;//ans是保底的数值,最少能覆盖1个点
        scanf("%d", &N);
        struct Point p[N];
        for(i=0;i<N;i++)
            scanf("%lf%lf", &p[i].x, &p[i].y);
        for(i=0;i<N;i++)
        {
            for(j=i+1;j<N;j++)
            {
                if(dist(p[i].x, p[i].y, p[j].x, p[j].y) > 2.0*R)
                    continue;//间距过大,超过了圆的直径,确定不了圆,跳过本次操作
                int k, count=2, num1=0, num2=0;//count初始置2,表示确定圆的两个点
                double c1 = 0, c2 = 0, A = 0, B = 0, C = 0, y01 = 0, x01 = 0, x02 = 0, y02 = 0;
                c1 = (pow(p[j].x, 2)-pow(p[i].x, 2)+pow(p[j].y, 2)-pow(p[i].y, 2))/2 / (p[j].x-p[i].x);
                c2 = (p[j].y-p[i].y) / (p[j].x-p[i].x);

                A = 1.0 + pow(c2, 2);
                B = 2*(p[i].x-c1)*c2 - 2*p[i].y;
                C = pow((p[i].x-c1), 2)+pow(p[i].y, 2)-pow(R, 2);

                y01 = (-B + sqrt(B*B - 4 * A * C)) / 2 / A;
                x01 = c1 - c2 * y01;//第一个圆的圆心

                y02 = (-B - sqrt(B*B - 4 * A * C)) / 2 / A;
                x02 = c1 - c2 * y02;//第二个圆的圆心
                for(k=0;k<N;k++)//第一个圆心覆盖的点数计算
                {
                    if(k==i || k==j)
                        continue;
                    if(dist(p[k].x, p[k].y, x01, y01) <= R)
                        num1++;
                }
                for(k=0;k<N;k++)//第二个圆心覆盖的点数计算
                {
                    if(k==i || k==j)
                        continue;
                    if(dist(p[k].x, p[k].y, x02, y02) < R)
                        num2++;
                }
                count += (num1>=num2) ? num1 : num2;//取能覆盖点数最多的圆保留,并加上在圆上的两个点
                ans = (count>=ans) ? count : ans;//最多能覆盖的点数
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

double dist(double x1, double y1, double x2, double y2)
{
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));//返回两点间的距离
}

? ? ? ? 当然,听有些大佬说用弧度来做就可以不用对确定的两个圆做取舍了。我也想优化一下,但考虑到我那可怜的数学成绩,Emm……一言难尽,想来这个做法也会很难,就没动手。

? ? ? ? 哪位好心的兄台这样做出来的话,欢迎留言来捞我一把,某感激不尽。ノ (○’ω’○)

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-21 15:43:11  更:2021-08-21 15:44:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:51:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码