【Py/Java/C++三种语言OD2023C卷真题】20天拿下华为OD笔试之【博弈论DP】2023C-抢7游戏【欧弟算法】全网注释最详细分类最全的华为OD真题题解
文章目录
- 题目描述与示例
-
- 题目描述
- 输入描述
- 输出描述
- 示例
-
- 输入
- 输出
- 说明
- 解题思路
- 代码
-
- Python
- Java
- C++
- 时空复杂度
- 华为OD算法/大厂面试高频题算法练习冲刺训练
题目描述与示例
题目描述
A、B两个人玩抢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起始选择10,B接着选择9,A接着选择8,B接着选择7赢得胜利。
解题思路
A和B每次选择的数字,只能比上一个数字小1或2,两个人所选择的数字越来越小直到降为7。这种计算组合数的问题很容易想到使用动态规划来解题。
我们考虑动态规划三部曲:
- dp数组的含义是什么?
由于A和B两个人是交替地进行数字选择的,我们可以构建两个dp数组,dpA和dpB。
dpA[i]表示A选择了第i个数字时的方法数,dpB[i]表示B选择了第i个数字时的方法数。
- 动态转移方程是什么?
由于两个人的选择是交替进行的,因此A的状态由先前的B转移得到,B的状态由先前的A转移得到。
当B选择了数字i时,上一轮A的选择必然是i+1或i+2。因此B选择数字i的组合数为A选择了数字i+1和i+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赢得最终胜利的方法数。
- dp数组如何初始化?
由于dp过程是从M-1开始的,当i = M-1时,需要使用到i+2 = M+1这个值,因此可以初始化dpA和dpB数组均为长度为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
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了解更多
本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/2c76994f2b.html
