GYM 100820 G.Racing Gems(LIS)

本文介绍了一个基于赛车模型的算法问题,赛车在限定区域内行驶并尽可能多地收集钻石。通过计算赛车路径与钻石位置的关系,转化为最长上升子序列问题,最终求解最多能收集到的钻石数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
在[0,w]x[0,h]的区域内赛车,初始时可以任意选取x轴上[0,w]内一点作为起点,只要到达y=h在[0,w]范围内任一点即为到达终点,赛车的垂直车速为v,水平车速可以在-v/r~v/r内任意变化,给出该区域内n个钻石的坐标,问在赛车过程中最多可以拿到多少钻石
Input
第一行输入四个整数n,r,w,h,之后n行每行两个整数表示一颗钻石的坐标(1<=n<=1e5,1<=r<=10,1<=w,h<=1e9,0<=x[i]<=w,0<=y[i]<=h)
Output
输出最多可以拿到的钻石数量
Sample Input
5 1 10 10
8 8
5 1
4 6
4 7
7 9
Sample Output
3
Solution
当赛车拿到一个钻石后,其下一个可以拿到的钻石所处范围必须要在以第i个点往上的斜率分别为-r和r的射线之间,设由第i个点发出的两条射线与左右边界交点的纵坐标分别为a[i]和b[i],那么拿到钻石i后可以拿到钻石j等价于钻石j在由第i个点发出的这两条射线之间,进一步等价于a[j]>=a[i],b[j]>=b[i],所以求出每个钻石发出射线与左右两边界的交点(a[i],b[i]),问题变成求这个二维序列的最长上升子序列长度,先按第一维升序排,对第二维求一个最长上升子序列即可
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 1e12
#define maxn 111111
int n,r,w,h;
struct node
{
    ll a,b;
    bool operator<(const node&c)const
    {
        if(a!=c.a)return a<c.a;
        return b<c.b;
    }
}p[maxn];
ll a[maxn],dp[maxn];
int LIS(ll a[])//求序列a的(非严格)最长上升子序列 
{
    for(int i=1;i<n;i++)dp[i]=INF;
    dp[0]=a[0];
    int len=1;
    for(int i=1;i<n;i++)
    {
        if(a[i]>=dp[len-1])dp[len++]=a[i];
        else dp[upper_bound(dp,dp+n,a[i])-dp]=a[i];
    }
    return len;
}
int main()
{
    while(~scanf("%d%d%d%d",&n,&r,&w,&h))
    {
        for(int i=0;i<n;i++)
        {
            ll x,y;
            scanf("%I64d%I64d",&x,&y);
            p[i].a=1ll*r*x+y,p[i].b=1ll*r*(w-x)+y;
        }
        sort(p,p+n);
        for(int i=0;i<n;i++)a[i]=p[i].b;
        printf("%d\n",LIS(a));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值