算法题 | 蓝桥杯 :田忌赛马

目录

题目描述

解题

 情况一:我们最快的马可以赢田忌最快的马

情况二:我们最快的马不能赢田忌最快的马

子情况2.1:我们最慢的马能赢田忌最慢的马 

 解释这个为什么:

子情况2.2:我们最慢的马不能赢田忌最慢的马

解释这个为什么是最优:

完整代码 

运行结果


题目描述

题源:洛谷

解题

思路:贪心算法+双指针

  1. 将双方马匹都排序

  2. 用我们最快的马尽可能赢对方最快的马

  3. 当无法赢时,用最差的马消耗对方最好的马

 情况一:我们最快的马可以赢田忌最快的马

        也就是我们的列表最大值(最后一个)的值大于田忌列表中最后一个:

our_horses[our_right] > tianji_horses[tianji_right]

        那么,赢得场次+1,快指针往前移动:

win += 1  
our_right -= 1  
tianji_right -= 1  

情况二:我们最快的马不能赢田忌最快的马

子情况2.1:我们最慢的马能赢田忌最慢的马 

our_horses[our_right] > tianji_horses[tianji_right]

        能赢,则赢得场次会+1,同时左指针均会右移

win += 1  
our_left += 1  
tianji_left += 1 
 解释这个为什么:

我们的最慢的马能稳赢对方最慢的马,这样能确保 至少拿到 1 胜

如果我们不这样做,可能会被迫用快马去赢慢马

  • 情况2.1 是在 无法用最快马赢对方最快马 时,检查 是否能用最慢马赢对方最慢马

  • 如果能,就 直接拿下这 1 胜,避免后续更糟的对局。

  • 这是 贪心策略的一部分,确保每一步都尽量获取最大收益。

情况2.1 和 情况2.2 共同作用,才能得到全局最优解! 🚀

子情况2.2:我们最慢的马不能赢田忌最慢的马

用我们最慢的马去消耗田忌最快的马(故意输掉这轮),让田忌最快的马被"兑子"掉,保留我们的好马去赢田忌的中等马

our_horses[our_right] <= tianji_horses[tianji_right] 
#注意相等也是没有赢

我们的左指针右移,田忌的右指针左移

our_left += 1  
tianji_right -= 1  
解释这个为什么是最优:

此时说明,我们的最慢的马已经无法赢任何马, our_horses[our_left] ≤ tianji_horses[tianji_left],说明我们的最慢的马比田忌最慢的马还慢,无法赢任何马。

与其让它去输给田忌最慢的马,不如让它去输给田忌最快的马,这样还能让田忌最快的马被消耗掉。相当于用最差的马换掉对方最强的马,保留我们的中等马去赢田忌的中等马。

举例说明

假设:

  • 我们的马(已排序): [1, 3, 5]

  • 田忌的马(已排序): [2, 4, 6]

写了情况2.2会:😊

        5<6 (不能赢)1<2 (不能赢)

        此时会拿我们的1和田忌的6比较,用1消耗掉6,最终3就可以PK掉田忌的2

        现在剩余马匹:我们:[3, 5]  田忌:[2, 4] 

        最终 5>4 3>2 胜2场✅

没有2.2的话:

         5<6 (不能赢)

        不执行2.2,而是直接跳过,尝试其他匹配方式,让 5 去对 4 → 5 > 4(赢),消耗 5 和 4

        现在剩余马匹:我们的 [1, 3] 田忌的 [2, 6]

        让 3 对 6 → 3 ≤ 6(不能赢)

        让 1 对 2 → 1 ≤ 2(不能赢)

        最终胜1场

完整代码 

# 读取输入的马匹数量
n = int(input())

# 读取我们的马匹速度列表并排序(升序)
our_horses = list(map(int, input().split()))
our_horses.sort()

# 读取田忌的马匹速度列表并排序(升序)
tianji_horses = list(map(int, input().split()))
tianji_horses.sort()

# 初始化获胜场次计数器
win = 0

# 初始化双指针:
# our_left/tianji_left 指向当前最慢的马(列表最左端)
# our_right/tianji_right 指向当前最快的马(列表最右端)
our_left = tianji_left = 0
our_right = tianji_right = n - 1

# 主循环:当还有未比赛的马时继续
while our_left <= our_right:
    # 情况1:我们最快的马能赢田忌最快的马
    if our_horses[our_right] > tianji_horses[tianji_right]:
        win += 1  # 赢得一场
        our_right -= 1  # 我们这匹马已用
        tianji_right -= 1  # 田忌这匹马已用
    
    # 情况2:我们最快的马不能赢田忌最快的马
    else:
        # 子情况2.1:我们最慢的马能赢田忌最慢的马
        if our_horses[our_left] > tianji_horses[tianji_left]:
            win += 1  # 赢得一场
            our_left += 1  # 我们这匹马已用
            tianji_left += 1  # 田忌这匹马已用
        
        # 子情况2.2:我们最慢的马也不能赢田忌最慢的马
        else:
            # 用我们最慢的马消耗田忌最快的马(必输策略)
            our_left += 1  # 我们这匹马已用
            tianji_right -= 1  # 田忌这匹马已用

# 输出最终能获胜的场次
print(win)

运行结果

AC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值