【Py/Java/C++三种语言OD2023C卷真题】20天拿下华为OD笔试之【博弈论DP】2023C-抢7游戏【欧弟算法】全网注释最详细分类最全的华为OD真题题解

题目描述与示例

题目描述

AB两个人玩抢7游戏,游戏规则为A先报一个起始数字X (10<X<10000),B报下一个数字Y,(0<X-Y<3),A再报一个数字Z(0<Y-Z<3),以此类推,直到其中一个抢到7,抢到7即为胜者,在B赢得比赛的情况下,一共有多少种组合?

输入描述

起始数字M,如100

10<=M<=10000

输出描述

B能赢得比赛的组合次数

示例

输入

10

输出

1

说明

只有一种赢的组合,A起始选择10B接着选择9A接着选择8B接着选择7赢得胜利。

解题思路

AB每次选择的数字,只能比上一个数字小12,两个人所选择的数字越来越小直到降为7。这种计算组合数的问题很容易想到使用动态规划来解题。

我们考虑动态规划三部曲:

  1. dp数组的含义是什么?

由于AB两个人是交替地进行数字选择的,我们可以构建两个dp数组,dpAdpB

dpA[i]表示A选择了第i个数字时的方法数,dpB[i]表示B选择了第i个数字时的方法数。

  1. 动态转移方程是什么?

由于两个人的选择是交替进行的,因此A的状态由先前的B转移得到,B的状态由先前的A转移得到。

B选择了数字i时,上一轮A的选择必然是i+1i+2。因此B选择数字i的组合数为A选择了数字i+1i+2的组合数的总和,即存在动态转移方程dpB[i] = dpA[i+1] + dpA[i+2]

对于A选择了数字i的情况,同理存在dpA[i] = dpB[i+1] + dpB[i+2]

由于选择是从大到小进行的,因此必须从M-1开始进行逆序遍历,直到选择了7。即

for i in range(M-1, 6, -1):
    dpB[i] = dpA[i+1] + dpA[i+2]
    dpA[i] = dpB[i+1] + dpB[i+2]

print(dpB[7])

最终dpB[7]就是B抢到了7赢得最终胜利的方法数。

  1. dp数组如何初始化?

由于dp过程是从M-1开始的,当i = M-1时,需要使用到i+2 = M+1这个值,因此可以初始化dpAdpB数组均为长度为M+2的一维数组(或哈希表也可以,因为小于7的那些位置实际上是没有用到的)。

由于A最开始报的数字是M,换句话说A取得数字M的方法数只有1种,因此初始化dpA[M] = 1,其余位置均初始化为0。即

dpA = [0] * (M+2)
dpB = [0] * (M+2)
dpA[M] = 1

上述过程用到了两个dp数组,看起来似乎比较冗长。

实际上可以把两个动态转移方程进行合并,只得到一个关于dpB的动态转移方程。将

dpA[i+1] = dpB[i+2] + dpB[i+3]dpA[i+2] = dpB[i+3] + dpB[i+4]代入dpB[i] = dpA[i+1] + dpA[i+2],可以得到新的动态转移方程dpB[i] = dpB[i+2] + 2*dpB[i+3] + dpB[i+4],这样就只需要一个dpB数组和一个动态转移方程即可。

整体的代码修改为

M = int(input())

dpB = [0] * (M+2)
dpB[M-1] = 1
dpB[M-2] = 1

for i in range(M-3, 6, -1):
    dpB[i] = dpB[i+2] + 2*dpB[i+3] + dpB[i+4]

print(dpB[7])

代码

Python

# 题目:【DP】2023C-抢7游戏
# 分值:100
# 作者:许老师-闭着眼睛学数理化
# 算法:DP
# 代码看不懂的地方,请直接在群上提问


# 输入A的初始报数
M = int(input())

# 初始化dpA和dpB两个初始数组
dpA = [0] * (M+2)
dpB = [0] * (M+2)
dpA[M] = 1

# dp过程,从M-1开始遍历,到7结束
for i in range(M-1, 6, -1):
    dpB[i] = dpA[i+1] + dpA[i+2]
    dpA[i] = dpB[i+1] + dpB[i+2]

# 输出dpB[7]即为最终答案
print(dpB[7])

Java

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 输入A的初始报数
        long M = scanner.nextLong();

        // 初始化dpA和dpB两个初始数组
        long[] dpA = new long[(int) (M + 2)];
        long[] dpB = new long[(int) (M + 2)];
        dpA[(int) M] = 1;

        // dp过程,从M-1开始遍历,到7结束
        for (int i = (int) (M - 1); i >= 6; i--) {
            dpB[i] = dpA[i + 1] + dpA[i + 2];
            dpA[i] = dpB[i + 1] + dpB[i + 2];
        }

        // 输出dpB[7]即为最终答案
        System.out.println(dpB[7]);
    }
}

C++

#include <iostream>
using namespace std;

int main() {
    // 输入A的初始报数
    long long M;
    cin >> M;

    // 初始化dpA和dpB两个初始数组
    long long dpA[M + 2] = {0};
    long long dpB[M + 2] = {0};
    dpA[M] = 1;

    // dp过程,从M-1开始遍历,到7结束
    for (int i = M - 1; i >= 6; i--) {
        dpB[i] = dpA[i + 1] + dpA[i + 2];
        dpA[i] = dpB[i + 1] + dpB[i + 2];
    }

    // 输出dpB[7]即为最终答案
    cout << dpB[7] << endl;

    return 0;
}

时空复杂度

时间复杂度:O(N)

空间复杂度:O(N)


华为OD算法/大厂面试高频题算法练习冲刺训练

  • 华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!

  • 课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化

  • 每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!

  • 60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁

  • 可上全网独家的欧弟OJ系统练习华子OD、大厂真题

  • 可查看链接 大厂真题汇总 & OD真题汇总(持续更新)

  • 绿色聊天软件戳 od1336了解更多

  • 33
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 20
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值