欢迎光临
我们一直在努力

蓝桥杯备赛打卡(Day4)

🌟 蓝桥杯备赛打卡(Day4) – 经典DP与组合计数:小蓝的现场问答 🏆

博主碎碎念: 大家好!这里是蓝桥杯备赛打卡 Day2!今天我们来死磕一道非常经典的组合计数与动态规划(DP)问题。这道题乍一看无从下手,但只要我们冷静下来,将大问题拆解为状态机的模式,找出状态转移的规律,就会豁然开朗。话不多说,直接看题!💪

一、 题目回顾

题目描述: 小蓝正在参与一个现场问答节目。活动中一共有 30 道题目,每题只有答对和答错两种情况。

  • 每答对一题得 10 分。
  • 答错一题分数归零。
  • 最高奖项需要 100 分,到达 100 分时直接停止答题。
  • 小蓝可以在任意时刻结束答题并领奖(可能不到 100 分)。 已知条件: 小蓝最终实际获得了 70分 对应的奖项。 问题: 请问小蓝所有可能的答题情况有多少种?

二、 核心解题思路(剥洋葱分析法)

想要最终停在 70 分,说明小蓝在答题结束时,状态必须被严格卡死。我们可以把答题过程当成一个由“对(O)”和“错(X)”组成的字符串。 要获得最终的 70 分,必须满足以下几个核心条件:

  • 收尾条件:最后 7 道题必须是全对(连得 70 分)。
  • 防越界条件:在最后的 7 连对之前,那一道题必须是答错(X)。如果是答对(O),那就变成 8 连对了,最后得分会变成 80 甚至更高。
    • 特例:小蓝总共就只答了 7 道题,且全对。
  • 全局合法条件:在前面的答题过程中,绝对不能出现 10 连对。因为一旦出现 10 连对,就达到 100 分提前结束比赛了,不可能再有后面的 70 分。
  • 总结答题序列的两种形态:

    • 形态一(秒杀局): 小蓝只答了 7 道题,序列为 OOOOOOO。这种情况只有 1 种。
    • 形态二(持久战): 小蓝总共答了

      i

      i

      i 道题(其中

      8

      i

      30

      8 \\le i \\le 30

      8i30)。

      • 最后的 8 道题被死死固定为:X OOOOOOO。
      • 前面剩下的前缀长度为

        i

        8

        i – 8

        i8。这个前缀序列随便怎么排,只要不出现连续的 10 个 O 即可。 🎯 破题关键: > 题目转化成了求:长度为 n 且不包含连续 10 个“对(O)”的序列总数。(其中 n 的取值范围是从 0 到 30−8=22)


    三、 动态规划(DP)推导

    我们设

    d

    p

    [

    i

    ]

    dp[i]

    dp[i] 为:长度为

    i

    i

    i 且不包含连续 10 个“O”的序列数量。 考虑我们在长度为

    i

    1

    i-1

    i1 的合法序列后追加一道题:

  • 追加 X(错):只要前面

    i

    1

    i-1

    i1 长度合法,追加 X 肯定合法。数量为

    d

    p

    [

    i

    1

    ]

    dp[i-1]

    dp[i1]

  • 追加 O(对):大多数情况下也是合法的,数量也是

    d

    p

    [

    i

    1

    ]

    dp[i-1]

    dp[i1]。但是!有可能会因为这最后一个 O,恰好在尾部凑成了 10 连对,导致序列失效。 什么时候会恰好凑成 10 连对? 当且仅当它前面的序列是以 X OOOOOOOOO(1 个错 + 9 个对)结尾的。 这种“导致失效”的序列前缀部分长度为

    i

    1

    9

    =

    i

    10

    i – 1 – 9 = i – 10

    i19=i10。 但要注意,这 9 个 O 前面的那个必然是 X(长度算在

    i

    11

    i-11

    i11 里面了),所以非法的组合数量等同于长度为

    i

    11

    i-11

    i11 的合法序列数量,即

    d

    p

    [

    i

    11

    ]

    dp[i-11]

    dp[i11]。 🔥 状态转移方程得出:

  • d

    p

    [

    i

    ]

    =

    2

    ×

    d

    p

    [

    i

    1

    ]

    d

    p

    [

    i

    11

    ]

    dp[i] = 2 \\times dp[i-1] – dp[i-11]

    dp[i]=2×dp[i1]dp[i11]

    初始状态处理:

    • i

      <

      10

      i < 10

      i<10 时,不可能凑出 10 连对,随便排:

      d

      p

      [

      i

      ]

      =

      2

      i

      dp[i] = 2^i

      dp[i]=2i

    • i

      =

      10

      i = 10

      i=10 时,总共有

      2

      10

      =

      1024

      2^{10} = 1024

      210=1024 种组合,只有 OOOOOOOOOO 这一种不合法:

      d

      p

      [

      10

      ]

      =

      1023

      dp[10] = 1023

      dp[10]=1023


    四、 Python3 代码实现

    理清了状态转移,代码简直是水到渠成。

    def solve():
    # 最大允许的前缀长度为 30 – 8 = 22
    max_prefix_len = 22

    # dp[i] 表示长度为 i 的序列中,不包含连续 10 个“对”的序列总数
    dp = [0] * (max_prefix_len + 1)

    # 前缀长度为0时,相当于空序列,只有1种情况
    dp[0] = 1

    for i in range(1, max_prefix_len + 1):
    if i <= 9:
    # 长度不到10,绝对不会触发10连对提前结束,直接翻倍
    dp[i] = dp[i1] * 2
    elif i == 10:
    # 恰好长度为10,需要减去全部都是“对”的这一种极端情况
    dp[i] = dp[i1] * 2 1
    else:
    # 长度大于10,在追加基础上,减去尾部恰好凑成10连对的非法序列数 (dp[i-11])
    dp[i] = dp[i1] * 2 dp[i11]

    # 总情况数 = 1 (特例:只答了7题且全对) + sum(dp) (所有可能的前缀情况总和)
    total_ways = 1 + sum(dp)

    print(f"小蓝所有可能的答题情况共有: {total_ways} 种")

    if __name__ == '__main__':
    solve()

    💡 运行结果

    执行上述代码,最终的答案是:8335366。


    五、 复杂度分析

    • 时间复杂度:

      O

      (

      N

      )

      O(N)

      O(N)。在这个问题中

      N

      N

      N 仅仅是前缀的最大长度 22。循环 22 次,时间复杂度极低,完全满足蓝桥杯对时间的苛刻要求。

    • 空间复杂度:

      O

      (

      N

      )

      O(N)

      O(N)。开辟了一个长度为 23 的一维 DP 数组,消耗的内存微乎其微。


    六、 备赛总结

    这道题其实是爬楼梯(斐波那契数列)的一种高阶变体。难点不在于代码怎么写,而在于前期对题目场景的解构能力。 做这类题目的小技巧:

  • 先确定终止状态的固定格式。
  • 再思考剩余部分的约束条件。
  • 最后用 DP / 递推 把约束条件具象化成数学公式。 Day4 顺利拿下!如果这篇笔记对你有帮助,欢迎点个赞支持一下~ 我们 Day5见!🚀🚀🚀
  • 赞(0)
    未经允许不得转载:171主机测评 » 蓝桥杯备赛打卡(Day4)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址