HDOJ:1007—Quoit Design
题目描述
题目分析
这个题目描述的情景应该是我们大家都熟知的套圈游戏,如下图:
这题意思就是:我们要拿一个环去套在地上的玩具,这个环大小只能套中一个,去求他的最大的半径。
输入是一系列玩具的坐标,输出是环的最大半径。
这一题,看似复杂,其实只是一个典型的==“寻找最短点对问题”==,就是去寻找玩具之间最短的距离,最容易想到的是暴力遍历就可以去求,但是那样时间复杂度太高,我们采用分治的方法。
题解
解题思路参考了《编程之美——微软技术面试心得》这本书中的"寻找最近点对中的内容"。
AC代码如下:
/**
* Problem 1007
* Quoit Design
* Date: 2023-4-15
*/
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAXSIZE = 100000;
const int L = -1;
const int R = 1;
//玩具的结构体,存储了玩具坐标
typedef struct{
double x; //x坐标
double y; //y坐标
int flag; //区域标记(L R)
}Tp;
Tp Toy[MAXSIZE];
Tp assit[MAXSIZE]; //辅助数组 用来统计左右区域的点
//计算两玩具之间距离
double get_distance(Tp &Toy1,Tp &Toy2){
return sqrt(pow(Toy1.x - Toy2.x,2)+pow(Toy1.y-Toy2.y,2));
}
//按照x从小到大排序
bool cmp_x(Tp &a,Tp &b){
if(a.x == b.x)
return a.y<b.y;
else
return a.x<b.x;
}
//按照y从小到大排序
bool cmp_y(Tp &a,Tp &b){
if(a.y == b.y)
return a.x<b.x;
else
return a.y<b.y;
}
//比较两个距离,返回小的
double min_dist(double &dist1,double &dist2){
if(dist1>dist2)
return dist2;
return dist1;
}
//比较三个距离返回小的
double min_three(double &dist1,double &dist2,double &dist3){
double min_d;
min_d = dist1>dist2 ? dist2:dist1;
min_d = min_d>dist3 ? dist3:min_d;
return min_d;
}
//分治法求最小距离(我们需要的半径)
double get_mindist(int low,int high){
double dist=0;
int count = high - low;
if(count==0){
return 0;
}else if(count==1){
//含有两个玩具
dist = get_distance(Toy[low],Toy[high]);
}else if(count==2){
//含有三个玩具
double toy1,toy2,toy3;
toy1 = get_distance(Toy[low],Toy[low+1]);
toy2 = get_distance(Toy[low],Toy[high]);
toy3 = get_distance(Toy[low+1],Toy[high]);
dist = min_three(toy1,toy2,toy3);
}else{
//大于三个以上的情况
double left_min,right_min,min;
int mid = (low+high)/2;
int p=0;
//求左边最小的距离
left_min = get_mindist(low,mid);
//求右边最小的距离
right_min = get_mindist(mid+1,high);
dist = min_dist(left_min,right_min);
/*找出在左区域和右区域的点,用flag来标记*/
//左区域
for(int i=low;i<=mid;i++){
double left_coord = Toy[mid].x - dist;
if(Toy[i].x>left_coord){
assit[p].flag = L; //属于左边部分
assit[p].x = Toy[i].x;
assit[p].y = Toy[i].y;
p++;
}
}
//右区域
for(int i=mid+1;i<=high;i++){
double right_coord = Toy[mid].x + dist;
if(Toy[i].x<right_coord){
assit[p].flag = R; //属于左边部分
assit[p].x = Toy[i].x;
assit[p].y = Toy[i].y;
p++;
}
}
//找到的点再按y坐标排序
sort(assit,assit+p,cmp_y);
for(int i=0;i<p;i++){
for(int j=1;(j<=7) && (i+j<p);j++){
if(assit[i].flag != assit[i+j].flag){
min = get_distance(assit[i],assit[i+j]);
if(min<dist)
dist = min;
}
}
}
}
return dist;
}
int main(){
int toy_num;
while(cin >> toy_num && toy_num!=0){
double radius = 0;
for(int i=0;i<toy_num;i++){
Toy[i].flag = 0;
cin >> Toy[i].x >> Toy[i].y;
}
sort(Toy,Toy+toy_num,cmp_x);
radius = get_mindist(0,toy_num-1) / 2; //其实求出的是直径,需要 /2
printf("%.2lf\n",radius);
}
}