蓝桥杯刷题017——轨道炮(贪心)

2019国赛轨道炮

题目描述

小明在玩一款战争游戏。地图上一共有 N 个敌方单位,可以看作 2D 平面上的点。其中第 i 个单位在 0 时刻的位置是 (Xi​,Yi​)方向是 Di​ (上下左右之一, 用'U'/'D'/'L'/'R' 表示),速度是 Vi​。

小明的武器是轨道炮,只能使用一次,不过杀伤力巨大。小明可以选择在某个非负整数时刻释放轨道炮,轨道炮一次可以消灭在一条直线 (平行于坐标轴)上的所有敌方单位。

请你计算小明最多能消灭多少敌方单位。

输入描述

输入第一行包含一个整数 N。

以下 N 行每行包含 3 个整数 Xi​,Yi​,Vi​,以及一个大写字符 Di​。

其中,1≤N≤1000,−10^6≤Xi​,Yi​≤10^6,0≤Vi​≤10^6。

输出描述

输出一个整数代表答案。

输入输出样例

输入

4
0 0 1 R
0 10 1 R
10 10 2 D
2 3 2 L

输出

3

题目大意:

平面上有N个点分别向上,下,左,右四个方向移动,每个点有速度v求解最多会有几个点会在同一行或同一列上(即纵坐标或横坐标相同)

题目样例图示如下,x轴上的两个点永远在同一行上,向下走的点在某一时刻也会到达x轴,这样最多有3个点在同一直线上。
 

每一个点有横纵坐标,速度,运动方向,这些信息如何存储?

创建两个列表存储每个点的横坐标和纵坐标(也可以创建一个列表,每个子列表存储每个点的坐标) ,两个列表存储每个点在横方向的速度和在纵方向的速度

X:[ ]        存储每个点的横坐标
Y:[ ]        存储每个点的纵坐标
vx:[ ]       存储每个点在横方向的速度(没有则为0)
vy:[ ]       存储每个点在纵方向的速度(没有则为0)

速度需要规定方向

  • 设定x方向向右为正,向左为负
  • 设定y方向向上为正,向下为负

难点︰如何求解最多会有几个点横/纵坐标相同

依次锁定每个点,看看在所有时刻中,它最多会和几个点横/纵坐标相同
依次计算锁定每个点后,取最大值即为最终结果

举例:有4个点
锁定1号:2个

锁定2号:1个

锁定3号:2个

锁定4号:3个

答案:3

锁定了一个点,如何计算最多的横/纵坐标相同点数?

做法:遍历其他所有点,首先判断它们是否会在某一时刻处于同一行/列
如果会,利用速度差\Delta v和距离差\Delta x/\Delta y,计算出同x/y的时刻t(例如下图①和②)

1、计算横坐标时,判断的是横坐标相同情况(同列) 

t=相对距离\Delta x//相对速度\Delta v(余数为0是才可能同x/y)

  • 相反方向:t = \frac{x_1-x_2}{v_2-v_1}

  • 相同方向: t = \frac{x_1-x_2}{v_2-v_1}

  

还有一部分点,永远和锁定的点处于同行/列。(例如下图②和③)这个要另外处理,因为分母\Delta v=0


2、计算纵坐标时,判断的是纵坐标相同情况(同行)

同上面计算横坐标的方法。

如何记录某一时刻t对应的同x/y的点数?

  • 用一个字典d记录每个点和锁定点同心/y的时刻t(字典d的键),然后d[t]+=1(默认空时是0),即可记录时刻t同x/y的点数(字典的值)
  • 有一部分点因为始终与锁定点同x/y,因此另外用一个变量always记录(不存在字典中,因为计算t时分母不能是0)计算一个坐标方向时,定义一个记录最大点数的变量mx,每个锁定点计算完时,从字典中获取最大的值+always,更新最大值。
  • 贪心思想:我们锁定每一个点,求它最多会和几个点共线,然后再找所有点的同x/y时刻t的点数的最大值。
  • 时间复杂度:O(n^2)
  • 最后对横纵坐标分别计算,取二者较大值,即为结果。

 代码:

n =int(input())
N = 1010
X = [0] * N; Y = [0] * N
vx = [0] * N; vy = [0] * N
for i in range(n):
    w = input().split()  # 每一点的x和y
    cx = int(w[0]); cy = int(w[1])
    X[i] = cx; Y[i] = cy 
    v = int(w[2])        # 每一点的速度
    # 四个方向
    if w[3] == 'U':
        vy[i] = v
    elif w[3] == 'D':
        vy[i] = -v
    elif w[3] == 'R':
        vx[i] = v
    elif w[3] == 'L':
        vx[i] = -v

def cal(P, v):
    mx = 0
    for i in range(n):
        d = {}              # 存储同x/y时刻t的点数
        aimx = P[i]; aimv = v[i]
        always = 1          # 始终与锁定点同x/y,锁定点本身就是一个,所以初始化为always = 1,
        for j in range(n):
            if i == j: continue     # 排除和自己的情况
            dx = aimx - P[j]        # 相对位置dx
            dv = v[j] - aimv        # 相对速度dv
            if dv == 0:             # 如果相对速度为0
                if dx == 0:         # 如果是始终与锁定点同x/y
                    always += 1
                mx = max(mx, always) # 更新时刻t的最大值
            else:                   # 相对速度不为0
                t = dx // dv        # 求出同x/y时刻t
                if dx % dv or t < 0:# 排除余数不为0或者时刻t<0
                    continue
                d[t] = d.get(t, 0) + 1          # 同x/y时刻t的点数+1
                mx = max(mx, d[t] + always)     # 更新时刻t的最大值
    return mx

res = max(cal(X, vx), cal(Y, vy))   # 取同x/y时刻t的点数的最大值就是结果
print(res)

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小叶pyか

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值