HNU2019 Summer Training 4 E. Fruit Slicer

Fruit Slicer

Problem Description


John, a student who is taking the game development course, recently developed a mobile game called Fruit Slicer for his coursework. In the game the player slices fruits that are throw into the air by swiping the touch screen. However the game is quite simple because John was not able to write code for the geometry required for a more complex version. In the game each slice is a straight line of infinite length, and all fruits have the same shape of a circle with unit ra- dius. The figure shows a cool snapshot of John’s game.

John introduces his game to his best friend Sean, who soon gets bored of playing the simple game. But as a teaching assistant of the algorithm course, Sean decides to turn the
game into a homework assignment. He asks the students in the algorithms course to write a program that can compute the best slice at any given moment of the game. Given the locations of the fruits, the program should determine the maximum number of fruits that can be sliced with a single straight-line swipe.

As a student in Sean’s class, you are now the one who is facing this challenge.

Input

The first line has a single integer n (1 ≤ n ≤ 100). The next n lines each have two real numbers giving the x and y coordinates of a fruit. All coordinates have an absolute value no larger than 10410^4104 and are given with exactly two digits after the decimal point. Fruits may overlap.

Output

Output the maximum number of fruits that can be sliced with one straight-line swipe. A swipe slices a fruit if the line intersects the inner part or the boundary of the fruit.

Sample Input

5
1.00 5.00
3.00 3.00
4.00 2.00
6.00 4.50
7.00 1.00

Sample Output

4

Sample Input2

3
-1.50 -1.00
1.50 -1.00
0.00 1.00

Sample Output2

3

Sample Input3

2
1.00 1.00
1.00 1.00

Sample Output3

2

Brief Description

确定单个直线能够最多与多少个单位元相交或者相切

Solution

我们知道,始终存在与两个圆相切的最佳线。

O(n2)枚举圆对,找出他们公切线,并为每个线性检查它相交或接触的圆的个数,更新最大值。


被浮点数精度折磨的要死,Debug4小时不是吹的,
题解给的是,只需要对系数*100,把所有开根号的比较转换为平方的比较,就可以用int做了

杂乱未加整理的代码:

#include <iostream>
#include <cmath>
double const PI = acos(-1);
using namespace std;
struct Point{
	double x,y;
	//Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Vector operator + (Vector A,Vector B){
	return Vector{A.x+B.x,A.y+B.y};
}
Vector operator - (Vector A,Vector B){
	return Vector{A.x-B.x,A.y-B.y};
}
Vector operator * (Vector A,double p){
	return Vector{A.x*p,A.y*p};
}
Vector operator / (Vector A,double p){
	return Vector{A.x/p,A.y/p};
}
bool operator < (const Point& a,const Point& b){
	return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
struct Line{
	Vector v;
	Point p;
};
struct Circle{
	Point c;
	double r;
	//Circle(Point c,double r):c(c),r(r){}
	//Point point(double a){
	//	return Point(c.x+cos(a)*r,c.y+sin(a)*r);
	//}
	
	Point getPoint(double a){
		//TODO:    
		return Point{c.x+cos(a)*r,c.y+sin(a)*r};
	}
	
};
const double eps = 1e-6;
int dcmp(double x){
	if(fabs(x)<eps) return 0;
	else return x<0?-1:1;
}
int dcmp2(double x){
	if(fabs(x)<eps) return 0;
	else return x<0?-1:1;
}
bool operator == (const Point& a,const Point& b){
	return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}

int getLineCircleIntersection(Line L,Circle C){
	double a = L.v.x,b=L.p.x - C.c.x,c=L.v.y,d=L.p.y-C.c.y;
	double e=a*a + c*c,f=2*(a*b+c*d),g=b*b+d*d-C.r*C.r;
	double delta = f*f - 4*e*g;
	if(dcmp2(delta)<0) return 0;
	if(dcmp2(delta)==0) return 1;
	return 2;
}


int getTangents(Circle A, Circle B, Point *a, Point *b){
	if (A.c==B.c){
		a[0]=A.c;
		b[0]=A.getPoint(0);
		return 1;
	}
	int cnt = 0;
	//if(A.r < B.r){
	//	swap(A, B);
	//	swap(a,b);
	//}
	double d2 = (A.c.x-B.c.x)*(A.c.x-B.c.x) + (A.c.y-B.c.y)*(A.c.y-B.c.y);
	double rDiff = 0.0;
	double rSum = 2.0;
	//if(dcmp(d2 - rDiff*rDiff)<0) return 0;
	
	double base = atan2(B.c.y - A.c.y, B.c.x - A.c.x);
	//if(d2 == 0 && A.r == B.r) return -1;
//	if(dcmp(d2 - rDiff*rDiff)==0){
//		a[cnt] = A.getPoint(base);
//		b[cnt] = B.getPoint(base);
//		++cnt;
//		return 1;
//	}
	
	double ang = acos(0);
	a[cnt] = A.getPoint(base + ang);
	b[cnt] = B.getPoint(base + ang);
	++cnt;
	a[cnt] = A.getPoint(base - ang);
	b[cnt] = B.getPoint(base - ang);
	++cnt;
	if(dcmp(d2 - 4.0) == 0){
		a[cnt] = A.getPoint(base);
		Vector t_v = A.c-B.c;
		if(dcmp(t_v.y)==0){
			b[cnt]= Point{a[cnt].x,a[cnt].y+1.0};
		}
		else{
			b[cnt] = Point{a[cnt].x+1.0,a[cnt].y-t_v.x/t_v.y};
		}
		++cnt;
	}
	else if(dcmp(d2 - 4.0)>0){
		double ang2 = acos((2.0)/sqrt(d2));
		a[cnt] = A.getPoint(base + ang2);b[cnt] = B.getPoint(PI+base+ang2);++cnt;
		a[cnt] = A.getPoint(base - ang2);b[cnt] = B.getPoint(PI+base-ang2);++cnt;
		
	}
	
	return cnt;
} 

const int maxn = 105;
int n;
Point poi[maxn];
Circle cir[maxn];
int main(){
	cin>>n;
	double x,y;
	for(int i=1;i<=n;i++){
		cin>>x>>y;
		poi[i].x=x;
		poi[i].y=y;
		cir[i].c.x=x;
		cir[i].c.y=y;
		cir[i].r=1.0;
	}
	Line l;
	int ans=2,cnt,ct;
	Point a[10],b[10];
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			//if(poi[i]==poi[j]) continue;
			//l.v=(poi[i]-poi[j]);
			//cout<<j<<endl;
			ct=getTangents(cir[i],cir[j],a,b);//cout<<ct<<endl;
			for(int d=0;d<ct;d++){
				cnt=0;
				l.v=(a[d]-b[d]);
				l.v=l.v/sqrt(l.v.x*l.v.x + l.v.y*l.v.y);
				l.p=b[d];
				for(int k=1;k<=n;k++){
					if(getLineCircleIntersection(l,cir[k])) cnt++;
					//else
				//cout<<a[d].x<<" "<<a[d].y<<"\n"<<b[d].x<<" "<<b[d].y<<endl;
				}
				ans=max(ans,cnt);//cout<<a[d].x<<" "<<a[d].y<<"\n"<<b[d].x<<" "<<b[d].y<<endl;
			}
		}
	}
	if(n==1) ans=1;
	cout<<ans;
	
	return 0;
}

/*
10
10000 10000
9999.99 9999.98
8888.98 8888.89
8888.99 8888.88
0.01 100.11
0.02 100.10
0.03 100.12
2.01 -100.99
2.02 -200.02
2.00 -9999.99
//cout<<a[d].x<<" "<<a[d].y<<"\n"<<b[d].x<<" "<<b[d].y<<endl;
2
45 23
789 11
2
5329.21 1848.86
3301.3 492.59
*/

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值