HDU 4116 Fruit Ninja (计算几何 +园+ 扫描线)
Fruit Ninja
Time Limit: 80000/40000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 853 Accepted Submission(s): 151
Problem Description
Fruit Ninja is a juicy action game enjoyed by millions of players around the world, with squishy, splat and satisfying fruit carnage! Become the ultimate bringer of sweet, tasty destruction with every slash.
--- wikipedia
It is a very popular game on cell phones where people can enjoy cutting the fruit by touching the screen. The screen is rectangular, and all the fruit can be considered as circles, with coordinate of the center, and radius. Note that the fruit may overlap with each other. In this problem, a touch is a straight line cutting through the whole screen, scoring all the fruits it cuts or touches.
Now Fred is playing the Fruit Ninja, and seems absorbed in the game. He's desperate to create a new record, so he asks you for help. Now you are given a screen shot of the game, help him find the highest score he can get in a single touch.
Input
The first line contains an integer T(1 <= T <= 50), indicating the number of test cases.
Each test case contains several lines.
The first line contains an integer N(1 <= N <= 1000), indicating the number of fruit.
The following N lines each contains three integers Xi,Yi,Ri(-1000 <= X,Y <= 1000,1 <= Ri <= 1000), representing a fruit on the screen, where (X,Y ) is the coordinate of the center of the fruit, and Ri is the radius.
You can assume the screen is infinite.
Each test case contains several lines.
The first line contains an integer N(1 <= N <= 1000), indicating the number of fruit.
The following N lines each contains three integers Xi,Yi,Ri(-1000 <= X,Y <= 1000,1 <= Ri <= 1000), representing a fruit on the screen, where (X,Y ) is the coordinate of the center of the fruit, and Ri is the radius.
You can assume the screen is infinite.
Output
For each test case in the input, print one line: "Case #X: Y", where X is the test case number (starting with 1) and Y is maximum number of fruit that you can cut in a single touch.
Sample Input
2 4 -2 5 1 5 5 1 -3 2 1 0 1 1 4 -4 5 1 3 2 1 -5 3 1 4 -3 1
Sample Output
Case #1: 3Case #2: 2
Source
题意:给你最多1000个圆,问画一条直线最多能与几个圆相交,相切也算。
思路:对每个圆枚举得到和其他圆切线斜率,两个切线之间的斜率范围是可以经过的。得到的许多斜率范围可以转化为区间覆盖,nlogn排序得解,总复杂度O(n^2logn)。
这个讲的详细:http://www.cnblogs.com/GBRgbr/archive/2013/10/04/3351361.html
同样的代码 交C++40s险过 交G++9s都不用......
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 1005;
const double EP = 1e-9;
const double PI = acos( -1.0 );
struct POINT
{
double x;
double y;
POINT(double a=0, double b=0)
{
x=a; //constructor
y=b;
}
};
struct Circle
{
POINT c; //圆心坐标
double r; //半径
Circle() {}
Circle( POINT c, double r ): c(c), r(r) {}
POINT getPoint( double theta ) //根据极角返回圆上一点的坐标
{
return POINT( c.x + cos(theta)*r, c.y + sin(theta)*r );
}
void readCircle()
{
scanf("%lf%lf%lf", &c.x, &c.y, &r );
return;
}
} cc[N];
double dist(POINT p1,POINT p2) // 返回两点之间欧氏距离
{
return( sqrt( (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) ) );
}
int sgn(double x)
{
if(fabs(x) < EP)return 0;
if(x < 0)return -1;
else return 1;
}
//调整到0~2*PI区间
double fix( double ang )
{
while ( sgn( ang ) < 0 ) ang += 2.0*PI;
while ( sgn( ang - PI - PI ) >= 0 ) ang -= 2.0*PI;
return ang;
}
struct Line
{
int id; //哪个圆的切线
int s; //扫入扫出线
double ang; //极角
Line() { }
Line( int id, int s, double ang ): id(id), s(s), ang(ang) { }
}line[N*4];
bool cmp( const Line& lhs, const Line& rhs )//对线进行极角排序,用于上个极角线排序
{
int tmp = sgn( lhs.ang - rhs.ang );
if ( tmp ) return tmp < 0;
else return lhs.s > rhs.s;
}
//获得切线的斜率
void GetTangent( Circle& A, Circle& B, int& id, int& sum, int& LineN, Line *L )
{
double dis = dist(A.c, B.c);
double base = atan2( B.c.y - A.c.y, B.c.x - A.c.x );
//A内含B
if (sgn( A.r - B.r - dis ) > 0) return;
//B内含+内切A
if (sgn( B.r - A.r - dis ) >= 0)
{
++sum;
return;
}
//外切+相交
double ang1 = asin((B.r - A.r) / dis);
double ang2 = asin((A.r + B.r) / dis);
if ( sgn( A.r + B.r - dis ) >= 0 )
{
L[LineN++] = Line(id, 1, fix(base - ang1 ));
L[LineN++] = Line(id, -1, fix(base + ang1 + PI));
return;
}
//相离
L[LineN++] = Line(id, 1, fix(base - ang1));
L[LineN++] = Line(id, -1, fix(base + ang2));
L[LineN++] = Line(id, 1, fix(base - ang2 + PI));
L[LineN++] = Line(id, -1, fix(base + ang1 + PI));
}
bool vis[N];
int solved(int n, int LineN, Line *L)
{
int res = 0;
int sum = 0;
memset(vis, false, sizeof(bool)*(n+4));
for (int i = 0; i<LineN+LineN; i++)//这里2倍的点使之成圈
{
int k = i % LineN;
int id = L[k].id;
int s = L[k].s;
if (s == 1)
{
if(!vis[id])
{
vis[id] = true;
sum++;
}
}
else
{
if(vis[id])
{
vis[id] = false;
sum--;
}
}
if (sum > res) res = sum;
}
return res;
}
int main()
{
int t,n,ca = 0;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0; i<n; i++)
{
cc[i].readCircle();
}
int ans = 0;
for(int i=0; i<n; i++)
{
int sum = 1;
int lineN = 0;
for(int j=0; j<n; j++)
{
if(i==j)continue;
GetTangent(cc[i],cc[j],j,sum,lineN,line);
}
sort(line,line+lineN,cmp);
sum += solved(n,lineN,line);
if (sum > ans) ans = sum;
}
printf( "Case #%d: %d\n", ++ca, ans);
}
return 0;
}
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 1005;
const double EP = 1e-9;
const double PI = acos( -1.0 );
struct POINT
{
double x;
double y;
POINT(double a=0, double b=0)
{
x=a; //constructor
y=b;
}
};
struct Circle
{
POINT c; //圆心坐标
double r; //半径
Circle() {}
Circle( POINT c, double r ): c(c), r(r) {}
POINT getPoint( double theta ) //根据极角返回圆上一点的坐标
{
return POINT( c.x + cos(theta)*r, c.y + sin(theta)*r );
}
void readCircle()
{
scanf("%lf%lf%lf", &c.x, &c.y, &r );
return;
}
} cc[N];
double dist(POINT p1,POINT p2) // 返回两点之间欧氏距离
{
return( sqrt( (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) ) );
}
int sgn(double x)
{
if(fabs(x) < EP)return 0;
if(x < 0)return -1;
else return 1;
}
//调整到0~2*PI区间
double fix( double ang )
{
while ( sgn( ang ) < 0 ) ang += 2.0*PI;
while ( sgn( ang - PI - PI ) >= 0 ) ang -= 2.0*PI;
return ang;
}
struct Line
{
int id; //哪个圆的切线
int s; //扫入扫出线
double ang; //极角
Line() { }
Line( int id, int s, double ang ): id(id), s(s), ang(ang) { }
}line[N*4];
bool cmp( const Line& lhs, const Line& rhs )//对线进行极角排序,用于上个极角线排序
{
int tmp = sgn( lhs.ang - rhs.ang );
if ( tmp ) return tmp < 0;
else return lhs.s > rhs.s;
}
//获得切线的斜率
void GetTangent( Circle& A, Circle& B, int& id, int& sum, int& LineN, Line *L )
{
double dis = dist(A.c, B.c);
double base = atan2( B.c.y - A.c.y, B.c.x - A.c.x );
//A内含B
if (sgn( A.r - B.r - dis ) > 0) return;
//B内含+内切A
if (sgn( B.r - A.r - dis ) >= 0)
{
++sum;
return;
}
//外切+相交
double ang1 = asin((B.r - A.r) / dis);
double ang2 = asin((A.r + B.r) / dis);
if ( sgn( A.r + B.r - dis ) >= 0 )
{
L[LineN++] = Line(id, 1, fix(base - ang1 ));
L[LineN++] = Line(id, -1, fix(base + ang1 + PI));
return;
}
//相离
L[LineN++] = Line(id, 1, fix(base - ang1));
L[LineN++] = Line(id, -1, fix(base + ang2));
L[LineN++] = Line(id, 1, fix(base - ang2 + PI));
L[LineN++] = Line(id, -1, fix(base + ang1 + PI));
}
bool vis[N];
int solved(int n, int LineN, Line *L)
{
int res = 0;
int sum = 0;
memset(vis, false, sizeof(bool)*(n+4));
for (int i = 0; i<LineN+LineN; i++)//这里2倍的点使之成圈
{
int k = i % LineN;
int id = L[k].id;
int s = L[k].s;
if (s == 1)
{
if(!vis[id])
{
vis[id] = true;
sum++;
}
}
else
{
if(vis[id])
{
vis[id] = false;
sum--;
}
}
if (sum > res) res = sum;
}
return res;
}
int main()
{
int t,n,ca = 0;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0; i<n; i++)
{
cc[i].readCircle();
}
int ans = 0;
for(int i=0; i<n; i++)
{
int sum = 1;
int lineN = 0;
for(int j=0; j<n; j++)
{
if(i==j)continue;
GetTangent(cc[i],cc[j],j,sum,lineN,line);
}
sort(line,line+lineN,cmp);
sum += solved(n,lineN,line);
if (sum > ans) ans = sum;
}
printf( "Case #%d: %d\n", ++ca, ans);
}
return 0;
}