HDU 4116 Fruit Ninja ( 计算几何 + 扫描线 )
给你最多1000个圆,问画一条直线最多能与几个圆相交,相切也算。
显然临界条件是这条线是某两圆的公切线,最容易想到的就是每两两圆求出所有公切线,暴力判断一下。
可惜圆有1000个,时间复杂度太高。
网上题解的做法是枚举每个“中心圆”,求出这个圆与其他圆的切线,然后按极角排序,扫一圈。
把每条切线看成扫入线——添加一个圆,或扫出线——删除一个圆。
形象一点就是一条与中心圆相切的直线,沿着中心圆滚了一圈,当这个直线碰到其他圆时,是添加了一个圆还是删除了一个圆。


PS:这题C++交死活TLE,G++才能交过。为什么为什么为什么orz……
PS2:这题我之前少判了一种情况(就是那个A内含B的情况),导致fix调整角度区间的时候进了死循环好像,然后TLE了很长时间,去掉fix之后又RE。然后我就来回注释这附近的代码交上去各种测,错使我在错误的地方调错调了很长时间。实际上只是因为没有把那种情况判出去,导致后面的运算非法。以后要多注意这种情况。
PS3:感觉这题还有很多细节,但是我说不清楚了,直接看代码吧……
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm> using namespace std; const int MAXN = ;
const double eps = 1e-;
const double PI = acos( -1.0 ); struct Point
{
double x, y;
Point( double x = , double y = ):x(x), y(y) { }
void readPoint()
{
scanf( "%lf%lf", &x, &y );
return;
}
}; int dcmp( double x ) //控制精度
{
if ( fabs(x) < eps ) return ;
else return x < ? - : ;
} double PointDis( Point a, Point b )
{
return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) );
} //调整到0~2*PI区间
double fix( double ang )
{
while ( dcmp( ang ) < ) ang += 2.0*PI;
while ( dcmp( ang - PI - PI ) >= ) 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) { }
}; bool cmp( const Line& lhs, const Line& rhs )
{
int tmp = dcmp( lhs.ang - rhs.ang );
if ( tmp ) return tmp < ;
else return lhs.s > rhs.s;
} 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;
}
}; //获得切线的斜率
void GetTangent( Circle& A, Circle& B, int& id, int& sum, int& LineN, Line *L )
{
double dis = PointDis( A.c, B.c );
double base = atan2( B.c.y - A.c.y, B.c.x - A.c.x ); //A内含B
if ( dcmp( A.r - B.r - dis ) > ) return; //B内含+内切A
if ( dcmp( B.r - A.r - dis ) >= )
{
++sum;
return;
}
/*
//A内切B
if ( dcmp( A.r - B.r ) > 0 && dcmp( dis - rdiff ) == 0 )
{
L[LineN++] = Line( id, 1, base - PI/2 - eps );
L[LineN++] = Line( id, -1, base - PI/2 + eps );
return;
}
*/
//外切+相交
double ang1 = asin( (B.r - A.r) / dis );
double ang2 = asin( (A.r + B.r) / dis );
if ( dcmp( A.r + B.r - dis ) >= )
{
L[LineN++] = Line( id, , fix( base - ang1 ) );
L[LineN++] = Line( id, -, fix( base + ang1 + PI ) );
return;
} //相离
L[LineN++] = Line( id, , fix( base - ang1 ) );
L[LineN++] = Line( id, -, fix( base + ang2 ) );
L[LineN++] = Line( id, , fix( base - ang2 + PI ) );
L[LineN++] = Line( id, -, fix( base + ang1 + PI ) ); return;
} bool vis[MAXN];
int solved( int cN, int LineN, Line *L )
{
int res = ;
int sum = ; memset( vis, false, sizeof(bool)*(cN+) ); for ( int i = ; i < LineN + LineN; ++i )
{
int k = i % LineN;
int id = L[k].id;
int s = L[k].s; if ( s == )
{
if ( !vis[id] )
{
vis[id] = true;
++sum;
}
}
else
{
if ( vis[id] )
{
vis[id] = false;
--sum;
}
}
if ( sum > res ) res = sum;
}
return res;
} Line L[MAXN << ];
Circle cc[MAXN];
int cN; int main()
{
//freopen( "in.txt", "r", stdin );
//freopen( "out.txt", "w", stdout );
int T, cas = ;
scanf( "%d", &T );
while ( T-- )
{
scanf( "%d", &cN );
for ( int i = ; i < cN; ++i )
cc[i].readCircle(); int ans = ;
for ( int i = ; i < cN; ++i )
{
int sum = ;
int LineN = ;
for ( int j = ; j < cN; ++j )
{
if ( i == j ) continue;
GetTangent( cc[i], cc[j], j, sum, LineN, L );
} sort( L, L + LineN, cmp );
sum += solved( cN, LineN, L );
if ( sum > ans ) ans = sum;
} printf( "Case #%d: %d\n", ++cas, ans);
}
return ;
}
HDU 4116 Fruit Ninja ( 计算几何 + 扫描线 )的更多相关文章
- HDU 4116 Fruit Ninja
http://acm.hdu.edu.cn/showproblem.php?pid=4116 题意:给N个圆,求一条直线最多能经过几个圆?(相切也算) 思路:枚举中心圆,将其他圆的切线按照极角排序,并 ...
- hdu 4000 Fruit Ninja 树状数组
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4000 Recently, dobby is addicted in the Fruit Ninja. ...
- hdu 4620 Fruit Ninja Extreme
Fruit Ninja Extreme Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Othe ...
- HDU 4620 Fruit Ninja Extreme 搜索
搜索+最优性剪枝. DFS的下一层起点应为当前选择的 i 的下一个,即DFS(i + 1)而不是DFS( cur + 1 ),cur+1代表当前起点的下一个.没想清楚,TLE到死…… #include ...
- hdu - 3952 Fruit Ninja(简单几何)
思路来自于:http://www.cnblogs.com/wuyiqi/archive/2011/11/06/2238530.html 枚举两个多边形的两个点组成的直线,判断能与几个多边形相交 因为最 ...
- HDU 4000 Fruit Ninja 树状数组 + 计数
给你N的一个排列,求满足:a[i] < a[k] < a[j] 并且i < j < k的三元组有多少个. 一步转化: 求出所有满足 a[i] < a[k] < a[ ...
- HDU 4620 Fruit Ninja Extreme(2013多校第二场 剪枝搜索)
这题官方结题报告一直在强调不难,只要注意剪枝就行. 这题剪枝就是生命....没有最优化剪枝就跪了:如果当前连续切割数加上剩余的所有切割数没有现存的最优解多的话,不需要继续搜索了 #include &l ...
- hdu 4620 Fruit Ninja Extreme(状压+dfs剪枝)
对t进行从小到大排序(要记录ID),然后直接dfs. 剪枝的话,利用A*的思想,假设之后的全部连击也不能得到更优解. 因为要回溯,而且由于每次cut 的数目不会超过10,所以需要回溯的下标可以利用一个 ...
- HDU 4620 Fruit Ninja Extreme 暴搜
题目大意:题目就是描述的水果忍者. N表示以下共有 N种切水果的方式. M表示有M个水果需要你切. W表示两次连续连击之间最大的间隔时间. 然后下N行描述的是 N种切发 第一个数字C表示这种切法可以切 ...
随机推荐
- 第51章 设置FLASH的读写保护及解除—零死角玩转STM32-F429系列
第51章 设置FLASH的读写保护及解除 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.co ...
- AngularJS THML DOM
AngularJS为HTML Dom元素属性提供了绑定应用数据的指令. data-ng-disabled指令直接提供了绑定应用程序的数据到HTML元素的disabled属性. <!DOCTYPE ...
- 【Java】多线程相关复习—— 线程的创建、名字、运行情况以及顺序控制(join方法) 【一】
一.创建线程的三种方式 · 继承Thread类 · 实现Runnable接口 · 实现Callable接口 二. 线程状态 · 线程名字 getName() · 线程活动情况 isAlive() · ...
- javaWeb基础 javascript bom5个对象
bom 也称为浏览器对象 browser object model(浏览器对象模型),由五个对象组成: Window:浏览器窗口 最顶层对象. Navigator :浏览器对 ...
- iOS MapKit地图
地图框架:#import <MapKit/MapKit.h> 基本属性和方法: 属性: 地图类视图:MKMapView 地图类型:MKMapType mapType 地图旋转:rotate ...
- 自定义动画函数JQuery实现
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- LVS基于DR模式搭建负载均衡群集
LVS -DR模式集群架构原理图
- LeetCode962. 最大宽度坡
问题:最大宽度坡 给定一个整数数组 A,坡是元组 (i, j),其中 i < j 且 A[i] <= A[j].这样的坡的宽度为 j - i. 找出 A 中的坡的最大宽度,如果不存在,返 ...
- Python 对象(type/object/class) 作用域 一等函数 (慕课--Python高级,IO并发 第二章)
在python中一共有两种作用域:全局作用域和函数作用域全局作用域:在全局都有效,全局作用域在程序执行时创建,在程序执行结束时销毁:所有函数以外的区域都是全局作用域:在全局作用域中定义的变量,都属于全 ...
- C语言进阶——注释符号12
注释可能大认为非常简单,但是注释其实在C语言中很关键,来看下面一段对话. ---学生: 老师,我觉得注释没有必要深究,因为很简单,对程序功能也没有影响. ---老师: 注释是C语言最重要的工具,我们先 ...