FOJ_2144_Shooting Game_贪心,尺取法解决多键值排序
2015-04-13 00:20
357 查看
晚上从七点睡到十一点,没救了
题意:
一个人在三维笛卡尔坐标系原点用防空炮打蚊子,N个蚊子,蚊子都进行匀速直线运动,知道每个蚊子的起始位置和速度(dx, dy, dz),单位为米每秒,防空炮在发动的一瞬间,可以打死半径R以内的所有蚊子。他想要尽可能多地打死蚊子,同时他希望尽量少开炮,两次开炮不需冷却,问他最多打死多少蚊子,同时要开多少炮。
Then T cases follow, each case starts with two integers N and R which describe above.
Then N lines follow, the ith line contains six integers ax, ay, az, dx, dy, dz. It means that at time 0, the ith mosquito is at (ax, ay, az) and it’s moving direction is (dx, dy, dz) which means that after time t this mosquito will be at (ax+dx*t, ay+dy*t,
ax+dz*t). You can assume that dx*dx + dy*dy+ dz*dz > 0.
1 <= T <= 50, 1 <= N <= 100000, 1 <= R <= 1000000
-1000000 <= ax, ay, az <= 1000000
-100 <= dx, dy, dz <= 100
The range of each coordinate is [-10086, 10086]
A is the number of mosquito Fat brother can shoot down.
B is the number of times Fat brother need to shoot.
由于要尽可能多地打死蚊子,那么就需要打死所有能进入半径R攻击球的蚊子,对于一个蚊子来说,在它在攻击范围内的任意时间都可以打死他,由于要尽可能少开炮,所以在它要飞出攻击范围的瞬间开炮,这样可以尽量让更多蚊子进入攻击范围。所以贪心算法就是,对所有蚊子按照飞出时间排序,遍历每只蚊子,打死飞入时间在这之前的所有蚊子。飞出时间、飞入时间自然用预处理算出来存储,算法是解点和球相交的一元二次方程。朴素实现这个贪心算法的话,对每个飞出时间,我们要遍历所有蚊子来删除所有飞入时间在这之前的,O(n^2)不可接受。解决方法是利用两表尺取法:记录另一个蚊子数组,只记录飞入时间(之前那个数组只需要记录飞出时间),给每个蚊子安排一个编号,开一个布尔数组记录对应编号蚊子是否活着,用一个光标记录飞入数组已经扫到了的位置,初始为0。遍历飞出时间,若当前蚊子没死,开一炮,从飞入时间光标开始移动光标往后扫,杀死所有飞入时间小于等于飞出时间的。在这个算法里,虽然也用到了二重循环,但内层循环光标不断,一共遍历O(n),外层循环也是O(n),总复杂度为O(n),算法最复杂的部分是O(nlogn)的排序和大数据输入,最终证明用int读入比double快一点造成了超时和不超时的区别。。。
代码如下:
题意:
一个人在三维笛卡尔坐标系原点用防空炮打蚊子,N个蚊子,蚊子都进行匀速直线运动,知道每个蚊子的起始位置和速度(dx, dy, dz),单位为米每秒,防空炮在发动的一瞬间,可以打死半径R以内的所有蚊子。他想要尽可能多地打死蚊子,同时他希望尽量少开炮,两次开炮不需冷却,问他最多打死多少蚊子,同时要开多少炮。
Input
The first line of the date is an integer T, which is the number of the text cases.Then T cases follow, each case starts with two integers N and R which describe above.
Then N lines follow, the ith line contains six integers ax, ay, az, dx, dy, dz. It means that at time 0, the ith mosquito is at (ax, ay, az) and it’s moving direction is (dx, dy, dz) which means that after time t this mosquito will be at (ax+dx*t, ay+dy*t,
ax+dz*t). You can assume that dx*dx + dy*dy+ dz*dz > 0.
1 <= T <= 50, 1 <= N <= 100000, 1 <= R <= 1000000
-1000000 <= ax, ay, az <= 1000000
-100 <= dx, dy, dz <= 100
The range of each coordinate is [-10086, 10086]
Output
For each case, output the case number first, then output two numbers A and B.A is the number of mosquito Fat brother can shoot down.
B is the number of times Fat brother need to shoot.
由于要尽可能多地打死蚊子,那么就需要打死所有能进入半径R攻击球的蚊子,对于一个蚊子来说,在它在攻击范围内的任意时间都可以打死他,由于要尽可能少开炮,所以在它要飞出攻击范围的瞬间开炮,这样可以尽量让更多蚊子进入攻击范围。所以贪心算法就是,对所有蚊子按照飞出时间排序,遍历每只蚊子,打死飞入时间在这之前的所有蚊子。飞出时间、飞入时间自然用预处理算出来存储,算法是解点和球相交的一元二次方程。朴素实现这个贪心算法的话,对每个飞出时间,我们要遍历所有蚊子来删除所有飞入时间在这之前的,O(n^2)不可接受。解决方法是利用两表尺取法:记录另一个蚊子数组,只记录飞入时间(之前那个数组只需要记录飞出时间),给每个蚊子安排一个编号,开一个布尔数组记录对应编号蚊子是否活着,用一个光标记录飞入数组已经扫到了的位置,初始为0。遍历飞出时间,若当前蚊子没死,开一炮,从飞入时间光标开始移动光标往后扫,杀死所有飞入时间小于等于飞出时间的。在这个算法里,虽然也用到了二重循环,但内层循环光标不断,一共遍历O(n),外层循环也是O(n),总复杂度为O(n),算法最复杂的部分是O(nlogn)的排序和大数据输入,最终证明用int读入比double快一点造成了超时和不超时的区别。。。
代码如下:
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <cmath> using namespace std; struct mosqin { double t; int idx; bool operator < (const mosqin &b) const { return t<b.t; } }; struct mosqout { double t; int idx; bool operator < (const mosqout &b) const { return t<b.t; } }; #define eps 1e-8 mosqin mosin[110000]; mosqout mosout[110000]; bool flag[110000]; int n; double r; int dcmp(double x) { return (x>eps)-(x<-eps); } int main() { int T,cas=0; scanf("%d",&T); while (T--) { scanf("%d%lf",&n,&r); memset(flag,true,sizeof(flag)); for (int i=0;i<n;i++) { mosin[i].idx=i,mosout[i].idx=i; int a1,a2,a3,d1,d2,d3; scanf("%d%d%d%d%d%d",&a1,&a2,&a3,&d1,&d2,&d3); //cout<<a1<<a2<<a3<<d1<<d2<<d3<<endl; double a=(double)d1*d1+(double)d2*d2+(double)d3*d3; double b=2*(double)a1*d1+2*(double)a2*d2+2*(double)a3*d3; double c=(double)a1*a1+(double)a2*a2+(double)a3*a3-r*r; double delt=b*b-4*a*c; if (delt<0) {flag[i]=false;continue;} double sdelt=sqrt(delt); //cout<<a<<" "<<b<<" "<<c<<" "<<sdelt<<endl; mosin[i].t=(-b-sdelt)/(2*a); mosout[i].t=(-b+sdelt)/(2*a); //cout<<mosin[i].t<<" "<<mosout[i].t<<endl; if (mosout[i].t<0) flag[i]=false; } sort(mosin,mosin+n); sort(mosout,mosout+n); int j=0,scnt=0,mcnt=0; for (int i=0;i<n;i++) { if (flag[mosout[i].idx]==false) continue; ++scnt; while (j<n && mosin[j].t<=mosout[i].t) { if (flag[mosin[j].idx]==false) {++j;continue;} ++mcnt; flag[mosin[j].idx]=false; ++j; } if (j>=n) break; } printf("Case %d: %d %d\n",++cas,mcnt,scnt); } return 0; }
相关文章推荐
- ACM学习历程—FZU 2144 Shooting Game(计算几何 && 贪心 && 排序)
- PHP实现对多维数组按照某个键值排序的两种解决方法
- FZU - 2144 Shooting Game(贪心,区间覆盖问题变题)
- fzu 2144 Shooting Game(贪心区间覆盖)
- fzu 2144 Shooting Game 区间覆盖贪心
- FZU 2144 Shooting Game(球体方程和直线方程联立+贪心区间覆盖)
- FZU 2144 Shooting Game(几何 + 贪心区域覆盖)
- js关于对象键值为数字型时输出的对象自动排序问题的解决方法
- FZU 2144 Shooting Game (贪心区域划分)
- FZU 2144 —— Shooting Game (贪心)
- FZU 2144 Shooting Game (贪心区域划分)
- PHP实现对多维数组按照某个键值排序的两种解决方法
- 不同database排序方式,不同语言,如何解决国际化编程问题
- 解决收藏夹收藏列表按字母排序问题
- “无法解决 equal to 操作的排序规则冲突。”的错误分析和解决方法
- 有关SQL排序规则---------即"无法解决 equal to 操作的排序规则冲突"等等的菜问题.
- 使用位图解决电话号码排序问题--编程珠玑学习笔记 第一章
- 解决lucene 1.* 使用排序后内存溢出问题
- 数组排序面试题php解决代码
- 无法解决 equal to 操作的排序规则冲突