您的位置:首页 > 其它

FOJ_2144_Shooting Game_贪心,尺取法解决多键值排序

2015-04-13 00:20 357 查看
晚上从七点睡到十一点,没救了

题意:

一个人在三维笛卡尔坐标系原点用防空炮打蚊子,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: