第九届河南省ACM省赛 D 导弹发射

D 导弹发射


Description

Alpha 机构研发出一种新型智能导弹,它能够在雷达检测到的区域内,选择一条前进的路径,
击破路径上所有的目标物。
雷达位于(0,0)处,它能够检测到两条射线之间的区域(不妨设在第一象限)。
导弹一开始置放在(0,0)处,它可以在雷达能检测到的区域内先选择一个目标物击破,然后
再继续前进,选择另一个目标物击破。注意,导弹不能沿着这两条射线前进,当然也不能停在原
地。
可以假设,导弹一旦发射,其能量无比大,前进的路径无限长。
已知雷达能够检测到区域,其射线 1:ax-by=0 和射线 2:cx-dy=0。Alpha 机构的总指挥希望
在发现目标群的第一时刻,计算出一条可以击破最多目标物的路径。

Input

第一行: T 表示以下有 T 组测试数据(1≤T ≤8)
对每组测试数据:
第 1 行: n 表示目标物的个数
第 2 行: a b c d 代表两条射线的斜率分别是 a/b 和 c/d。
接下来有 n 行,每行 2 个正整数 xi yi 即第 i 个目标物的坐标。
(1) n<=10^5 0<=a, b, c, d<=10^5 a 和 b 不会同时为 0,c 和 d 不会同时为 0;
(2) 0<= xi , yi <=10^6 i=1,…..,n

Output

每组测试数据,输出占一行,即导弹能击破的最多目标数。

Sample Input

1
15
1 3 2 1
3 1
6 2
4 2
2 5
4 5
6 6
3 4
1 6
2 1
7 4
9 3
5 3
1 3
15 5
12 4

Sample Output

4


解题思路:
     首先,我们只需要考虑在曲线之内的点。然后思考,如何判断导弹是前进的?(这个问题在比赛的时候一直困扰我)我们将两条射线看成x轴和y轴进行坐标变换,有了新的坐标后,很容易可以判断前进的情况(x递增,y递增)。变换之后用nlogn的LIS求解即可。这里要注意一个问题,当x轴不变,y轴增大时,LIS有可能将其统计在内。我们在排序时设置,当x相等时y从大到小排序,可以解决上述问题。
     这题的题面其实表达的不是很清楚,比如是否有多个目标在同一位置,两条射线的相对位置是否固定。。。然而题目在不考虑这些因素的情况下都能A。。。



代码:

#include <iostream>
#include<bits/stdc++.h>

using namespace std;
const int N=110000;

struct node
{
    double x,y;
}a[N],b[N];

bool cmp(node a,node b)//先按x从小到大排序,如果x相等按y从大到小排序,这样排除了沿y轴前进的情况
{
    if(a.x==b.x)    return a.y>b.y;
    return a.x<b.x;
}

int Search(int num,int low,int high)
{
    int mid;
    while(low<=high)
    {
        mid=(low+high)/2;
        if(a[num].y>b[mid].y) low=mid+1;
        else    high=mid-1;
    }
    return low;
}

int dp(int n)//LIS nlogn模板
{
    int i,len,pos;
    b[1].x=a[1].x;
    b[1].y=a[1].y;
    len=1;
    for(int i=2;i<=n;i++)
    {
        //两个点在同一位置的情况,数据中没有
        //if(a[i].y>b[len].y&&a[i].x>b[len].x||a[i].x==b[len].x&&a[i].y==b[len].y)
        if(a[i].y>b[len].y&&a[i].x>b[len].x)
        {
            len=len+1;
            b[len].y=a[i].y;
            b[len].x=a[i].x;
        }
        else
        {
            pos=Search(i,1,len);
            b[pos].y=a[i].y;
            b[pos].x=a[i].x;
        }
    }
    return len;
}

int main()
{
    int T,n;
    double a1,b1,c1,d1;
    cin>>T;
    while(T--)
    {
        cin>>n;
        cin>>a1>>b1>>c1>>d1;
        //保证c1/d1射线在a1/b1上面,数据中不存在相反情况
        /*if(b1==0||c1==0||(b1!=0&&d1!=0&&c1/d1<a1/b1))
        {
            swap(a1,c1);
            swap(b1,d1);
        }*/
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            double x,y;
            scanf("%lf%lf",&x,&y);
            //如果点在两个射线之内
            if(y*d1<x*c1&&x*a1<y*b1)
            {
                cnt++;
                //坐标变换
                a[cnt].x=(x-y*d1/c1)*c1/sqrt(c1*c1+d1*d1);
                a[cnt].y=(y-x*a1/b1)*b1/sqrt(a1*a1+b1*b1);
            }
        }
        sort(a+1,a+cnt+1,cmp);
        cout<<dp(cnt)<<endl;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值