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

news/2024/7/20 17:15:31 标签: java, c++, 华为od, leetcode, python, 算法

文章目录

  • 题目描述与示例
    • 题目描述
    • 输入描述
    • 输出描述
    • 示例
      • 输入
      • 输出
      • 说明
  • 解题思路
  • 代码
    • Python
    • Java
    • C++
    • 时空复杂度
  • 华为OD算法/大厂面试高频题算法练习冲刺训练

题目描述与示例

题目描述

中秋节,公司分月饼,m个员工,买了n个月饼,m <= n,每个员工至少分1个月饼,但可以分多个,单人分到最多月饼的个数是Max1,单人分到第二多月饼个数是Max2Max1-Max2 <= 3,单人分到第n-1多月饼个数是Max(n-1),单人分到第n多月饼个数是Max(n)Max(n-1)- Max(n) <= 3,问有多少种分月饼的方法?

输入描述

每一行输入m n,表示m个员工,n个月饼,m<=n

输出描述

输出有多少种月饼分法

示例

输入

2 4

输出

2

说明

分法有2种:

4=1+3
4=2+2

注意: 1+33+1算一种分法

解题思路

用比较严谨的数学语言表达上述问题为:挑选出m非递减的数,要求相邻两个数的差值不超过3,这m个数的和为n,问一共有多少种挑选方式。

由于在挑选完第i个数之后,第i+1个数的取值和第i个数的取值相关,很容易想到用动态规划来解决上述问题。思考动态规划三部曲:

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

我们需要考虑三个因素,选择了第几个数,这个数取了什么数值,当前总和为多少。因此需要构建一个三维的dp数组。

考虑dp[i][j][k]表示,第i个数取值为j时,前i个数的总和为k的方法数。

分别考虑ijk三个数的取值范围来确定dp数组的大小。

ik的定义比较明确,范围分别是[1, m][1, n]

每个数字的取值j的最小为1,最大的情况为m-1个数字都选择最小数1,剩余一个最大数选择n-(m-1) = n-m+1。故j的取值范围是[1, n+m+1]

故构建dp数组是一个大小为(m+1)*(n-m+2)*(n+1)的三维数组。

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

假设第i个数的取值为j,那么第i+1个数只能在jj+1j+2j+3中进行挑选。

若此时前i个数的总和为k,那么当第i+1个数

  • 取了j时,前i+1个数的总和为k+j。存在dp[i+1][j][k+j] += dp[i][j][k]
  • 取了j+1时,前i+1个数的总和为k+j+1。存在dp[i+1][j+1][k+j+1] += dp[i][j][k]
  • 取了j+2时,前i+1个数的总和为k+j+2。存在dp[i+1][j+2][k+j+2] += dp[i][j][k]
  • 取了j+3时,前i+1个数的总和为k+j+3。存在dp[i+1][j+3][k+j+3] += dp[i][j][k]

上述四个式子可以合并为一个式子,即dp[i+1][j+d][k+j+d] += dp[i][j][k],其中d的取值为[0,3]

先从小到大遍历i,再从小到大遍历j,再从小到大遍历k,则代码如下

python">for i in range(1, m):
    for j in range(1, n-m+2):
        for k in range(i, n+1):
            for d in range(4):
                if j+d < n-m+2 and k+j+d < n+1:
                    dp[i+1][j+d][k+j+d] += dp[i][j][k]
  1. dp数组如何初始化?

i = 0没有实际意义,不考虑。

考虑i = 1的情况,第1个数字的取值j最小为1,最大为n // m(即所有数字尽可能接近的情况),即此时j的取值为[1, n // m]

同时,由于只选择了一个数字,因此此时前i个数字的总和k = j

故对于i = 1,做如下初始化

python">dp = [[[0] * (n+1) for j in range(n-m+2)] for i in range(m+1)]
for j in range(1, n//m+1):
    dp[1][j][j] = 1

dp[1][j][j] = 1表示只对应1种方法数。

代码

Python

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


# 输入员工人数m,月饼总数n
m, n = map(int, input().split())

# dp数组是一个大小为(m+1)*(n-m+2)*(n+1)的三维数组
# dp[i][j][k]表示,第i个数取值为j时,前i个数的总和为k的方法数
dp = [[[0] * (n+1) for j in range(n-m+2)] for i in range(m+1)]
# 第1个数字选了j,此时总和为j
# j的取值范围是[1, n//m]
# 因为m个数的和需要为n,那么最小那个数的最大值是n//m
for j in range(1, n//m+1):
    dp[1][j][j] = 1

# i的最大取值为m-1
for i in range(1, m):
    # j的最小取值为1,最大取值为n-m+1
    for j in range(1, n-m+2):
        # k的最小取值为i(前i个数都选了1,和为i),最大取值为n
        for k in range(i, n+1):
            # 增量d的取值范围为0,1,2,3
            for d in range(4):
                # 条件为j+d和k+j+d都没有超过对应的最大范围
                if j+d < n-m+2 and k+j+d < n+1:
                    dp[i+1][j+d][k+j+d] += dp[i][j][k]

ans = 0
# dp[m][j][n]表示第m个数(最后一个数)选了j后,总和为n的方法数
# 将所有的dp[m][j][n]加在一起即为答案
for j in range(n-m+2):
    ans += dp[m][j][n]

print(ans)

Java

java">import java.util.Scanner;

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

        // dp array initialization
        int[][][] dp = new int[m + 1][n - m + 2][n + 1];
        for (int j = 1; j <= n / m; j++) {
            dp[1][j][j] = 1;
        }

        for (int i = 1; i < m; i++) {
            for (int j = 1; j <= n - m + 1; j++) {
                for (int k = i; k <= n; k++) {
                    for (int d = 0; d < 4; d++) {
                        if (j + d < n - m + 2 && k + j + d <= n) {
                            dp[i + 1][j + d][k + j + d] += dp[i][j][k];
                        }
                    }
                }
            }
        }

        int ans = 0;
        for (int j = 1; j <= n - m + 1; j++) {
            ans += dp[m][j][n];
        }

        System.out.println(ans);
    }
}

C++

#include <iostream>
using namespace std;

int main() {
    int m, n;
    cin >> m >> n;

    // dp array initialization
    int dp[m + 1][n - m + 2][n + 1] = {0};
    for (int j = 1; j <= n / m; j++) {
        dp[1][j][j] = 1;
    }

    for (int i = 1; i < m; i++) {
        for (int j = 1; j <= n - m + 1; j++) {
            for (int k = i; k <= n; k++) {
                for (int d = 0; d < 4; d++) {
                    if (j + d < n - m + 2 && k + j + d <= n) {
                        dp[i + 1][j + d][k + j + d] += dp[i][j][k];
                    }
                }
            }
        }
    }

    int ans = 0;
    for (int j = 1; j <= n - m + 1; j++) {
        ans += dp[m][j][n];
    }

    cout << ans << endl;

    return 0;
}

时空复杂度

时间复杂度:O(NM(N-M))。三重循环所需时间复杂度。

空间复杂度:O(NM(N-M))。三维dp数组所需空间。


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

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

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

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

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

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

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

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


http://www.niftyadmin.cn/n/5329126.html

相关文章

设计模式——一文即可

对常用设计模式的总结&#xff0c;也是对设计模式专栏的总结 简单工厂模式 简单工厂模式&#xff08;Simple Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种创建对象的最佳方式&#xff0c;通过将对象的创建逻辑封装在一个工厂类中&#xff0c;客…

ansible 配置文件详解+性能优化

Ansible 配置文件详解 常用参数详解&#xff1a; [defaults]通用默认配置段&#xff1b;inventory /etc/ansible/hosts被控端 IP 或者 DNS 列表;library /usr/share/my_modules/Ansible 默认搜寻模块的位置&#xff1b;remote_tmp $HOME/.ansible/tmpAnsible 远程执行临时…

59.说一下 spring 的事务隔离?

spring 的事务隔离有什么作用? 用来解决并发事务所产生一些问题,并发会产生什么问题? 1.脏读2.不可重复度3.幻影读事务隔离的概念 通过设置隔离级别可解决在并发过程中产生的那些问题分别举例说明 1.脏读 上述图表示:一个事务,读取了另一个事务中没有提交的数据,会在…

蓝桥杯AcWing学习笔记 8-1数论的学习(上)

蓝桥杯 我的AcWing 题目及图片来自蓝桥杯C AB组辅导课 数论&#xff08;上&#xff09; 蓝桥杯省赛中考的数论不是很多&#xff0c;这里讲几个蓝桥杯常考的知识点。 欧几里得算法——辗转相除法 欧几里得算法代码&#xff1a; import java.util.Scanner ;public class Main…

openssl3.2 - 官方demo学习 - kdf - hkdf.c

文章目录 openssl3.2 - 官方demo学习 - kdf - hkdf.c概述笔记END openssl3.2 - 官方demo学习 - kdf - hkdf.c 概述 设置摘要算法HKDF的参数, 然后取key 笔记 /*! \file hkdf.c \note openssl3.2 - 官方demo学习 - kdf - hkdf.c 设置摘要算法HKDF的参数, 然后取key *//** C…

AE修剪路径怎么用?

AE修剪路径怎么用?直线怎样做伸展动画呢&#xff1f;今天就教大家如何操作。 如图&#xff0c;让画面上的直线做伸展动画。 点击左下角的“添加”。 点击“修剪路径”。 文章源自设计学徒自学网-https://www.sx1c.com/38146.html 进度条移动到最开始&#xff0c;再点击左边结…

2024年1月16日

1 js显 隐类型转化 JavaScript中的类型转换分为两种&#xff1a;隐式类型转换和显式类型转换。 隐式类型转换 隐式类型转换通常发生在运算符进行计算时&#xff0c;JavaScript引擎会自动进行类型转换以执行运算。例如&#xff0c;当你将一个数字与一个字符串进行加法运算时&…

Go自研微服务框架-参数处理

参数处理 1. 频繁创建context的优化 sync.Pool用于存储那些被分配了但是没有被使用&#xff0c;但是未来可能被使用的值&#xff0c;这样可以不用再次分配内存&#xff0c;提高效率。 sync.Pool大小是可伸缩的&#xff0c;高负载是会动态扩容&#xff0c;存放在池中不活跃的…