华为OD机试 - 商品推荐 - 深度优先搜索DFS(Java 2024 D卷 200分)

在这里插入图片描述

华为OD机试 2024D卷题库疯狂收录中,刷题点这里

专栏导读

本专栏收录于《华为OD机试(JAVA)真题(D卷+C卷+A卷+B卷)》

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

一、题目描述

某电商APP希望基于用户的商品浏览历史记录,基于商品的热度值,给用户推荐一些关联的商品。

用户浏览过的商品和类似商品会关联在一起,每个商品最多有3个商品进行关联。每个商品都有一个热度值,是系统基于当前商品的购买量和搜索热度等计算出来的值。

现给出用户浏览的商品关联信息,找出最合适的推荐商品序列,要其推荐序列中商品热度值总和最高,并且返回该热度值。

二、输入描述

第一行:商品关联数组Q的长度,长度取值是[1,10000];

第二行:商品关联数组,其中除第一个元素单独一组外,其他每连续三个元素一组,为前面商品的关联商品;

数组里面值表示商品的热度值,取值范围[-2000, 2000],其中-1表示这个商品没有和其他商品进行关联。

注意:商品关联关系之间不存在环。

输入中的-1表示这个商品没有和其他商品进行关联

三、输出描述

现在基于用户浏览的商品关联信息,找出最合适的推荐商品序列,推荐序列中商品热度值总和最高,并且返回该热度值。

注意:每个商品只能在推荐序列中出现一次。

四、测试用例

测试用例1:

1、输入

19
20 12 30 15 -1 -1 -1 -1 -1 -1 15 5 25 -1 -1 -1 16 -1 22

2、输出

92

3、说明

在这里插入图片描述
我们要找出从根节点开始路径和最大的路径。在这个例子中,我们需要考虑所有可能的路径和选择其中最大的一个。

从根节点20开始,有以下几条可能的路径:

20 -> 12:20 + 12 = 32
20 -> 30:20 + 30 = 50
20 -> 15 -> 15:20 + 15 + 15 = 50
20 -> 15 -> 5 -> 16:20 + 15 + 5 + 16 = 56
20 -> 15 -> 5 -> 22:20 + 15 + 5 + 22 = 62
20 -> 15 -> 5:20 + 15 + 5 = 40
20 -> 15 -> 25:20 + 15 + 25 = 60

计算每条路径的和:

20 -> 12:20 + 12 = 32
20 -> 30:20 + 30 = 50
20 -> 15 -> 15:20 + 15 + 15 = 50
20 -> 15 -> 5 -> 16:20 + 15 + 5 + 16 = 56
20 -> 15 -> 5 -> 22:20 + 15 + 5 + 22 = 62
20 -> 15 -> 5:20 + 15 + 5 = 40
20 -> 15 -> 25:20 + 15 + 25 = 60

计算路径和最大的路径是:20 -> 15 -> 5 -> 22,总和为92。

五、解题思路

1、数据结构与算法

在constructTree方法中使用BFS来构建树的结构。BFS通过层次遍历节点,从根节点开始依次将每个节点的子节点添加到队列中,直至遍历完所有节点。

在calculateMaxPathSum方法中使用DFS来计算树中的最大路径和。DFS从根节点开始,递归遍历每个节点及其子节点,计算每条路径的和,并更新全局最大路径和。

通过使用数组、列表、队列等数据结构,以及广度优先搜索(BFS)和深度优先搜索(DFS)算法,该代码实现了从输入构建三叉树结构,并计算树中最大路径和的功能。DFS递归遍历树中的每一个节点,计算每条路径的和,并更新全局最大路径和。BFS用于按层次构建树的结构,以便后续的DFS遍历。

2、具体步骤:

给定一个表示商品浏览历史的关联数组,每个商品最多有三个关联商品,每个商品有一个热度值。目的是找到一个推荐商品序列,使得该序列中商品热度值总和最高,并返回这个最大热度值。

输入的第一行是关联数组的长度。

输入的第二行是关联数组,每个商品最多有三个关联商品,关联商品的热度值可能为-1(表示没有关联商品)。

使用广度优先搜索(BFS)构建三叉树,将输入的关联数组转换为树结构。

父节点索引为 i,其子节点索引分别为 3i+1,3i+2,3*i+3。

使用深度优先搜索(DFS)从根节点开始遍历树,计算每条路径的热度值总和。

在遍历过程中更新全局最大路径和。

最终输出全局最大路径和。

六、Java算法源码

public class OdTest {
    static ArrayList<Integer> treeNodes; // 存储树的节点值
    static int maxPathSum = Integer.MIN_VALUE; // 用于记录全局最大路径和

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

        // 读取节点数量
        int nodeCount = Integer.parseInt(scanner.nextLine());
        // 读取节点值数组
        int[] nodeValues = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();

        // 构建三叉树结构
        constructTree(nodeCount, nodeValues);
        // 计算树中的最大路径和
        calculateMaxPathSum(0);

        // 输出最大路径和
        System.out.println(maxPathSum);
    }

    /**
     * 构建三叉树结构
     * 父节点索引i,左子节点索引3*i+1,中子节点索引3*i+2,右子节点索引3*i+3
     */
    public static void constructTree(int nodeCount, int[] nodeValues) {
        treeNodes = new ArrayList<>(); // 初始化树节点列表

        LinkedList<Integer> nodeQueue = new LinkedList<>(); // 用于构建树的队列
        nodeQueue.add(nodeValues[0]); // 将根节点添加到队列

        int currentIndex = 1; // 当前处理的节点索引
        while (!nodeQueue.isEmpty() && currentIndex < nodeCount) {
            int currentNode = nodeQueue.removeFirst(); // 取出当前节点
            treeNodes.add(currentNode); // 添加到树节点列表

            if (currentNode != -1) { // 如果当前节点不是-1
                // 添加当前节点的三个子节点
                for (int i = 0; i < 3; i++) {
                    nodeQueue.add(nodeValues[currentIndex++]);
                }
            } else {
                // 如果当前节点为-1,添加三个-1作为占位符
                nodeQueue.add(-1);
                nodeQueue.add(-1);
                nodeQueue.add(-1);
            }
        }

        // 将剩余的节点添加到树节点列表中
        treeNodes.addAll(nodeQueue);
    }

    /**
     * 深度优先搜索计算最大路径和
     *
     * @param nodeIndex 当前处理的节点索引
     * @return 当前节点作为子树可以贡献的最大路径和
     */
    public static int calculateMaxPathSum(int nodeIndex) {
        // 如果节点不存在(值为-1),返回0
        if (treeNodes.get(nodeIndex) == -1) return 0;

        // 计算左、中、右子树的最大路径和
        int leftSum = getChildPathSum(nodeIndex, 1);
        int midSum = getChildPathSum(nodeIndex, 2);
        int rightSum = getChildPathSum(nodeIndex, 3);

        // 计算当前节点所有子树的路径和
        int totalSum = leftSum + midSum + rightSum;
        // 找到最小和最大的子树路径和
        int minChildSum = Math.min(Math.min(leftSum, midSum), rightSum);
        int maxChildSum = Math.max(Math.max(leftSum, midSum), rightSum);

        // 更新全局最大路径和
        // 当前节点为根的最大路径和 = 当前节点值 + 最大的两个子树和
        maxPathSum = Math.max(maxPathSum, treeNodes.get(nodeIndex) + totalSum - minChildSum);

        // 返回当前节点作为子树可以贡献的最大路径和(当前节点值 + 最大的子树和)
        return treeNodes.get(nodeIndex) + maxChildSum;
    }

    /**
     * 获取子节点的路径和
     *
     * @param parentIndex 父节点索引
     * @param childOffset 子节点在父节点中的偏移量
     * @return 子节点的最大路径和
     */
    private static int getChildPathSum(int parentIndex, int childOffset) {
        int childIndex = 3 * parentIndex + childOffset; // 计算子节点索引
        if (childIndex < treeNodes.size()) { // 如果子节点索引在范围内
            return Math.max(0, calculateMaxPathSum(childIndex)); // 递归计算子节点路径和,并返回非负值
        }
        return 0; // 如果子节点索引超出范围,返回0
    }
}

七、效果展示

1、输入

10
20 -50 -1 15 30 -1 -1 10 -1 8

2、输出

45

3、说明

在这里插入图片描述

我们要找出从根节点开始路径和最大的路径。在这个例子中,我们需要考虑所有可能的路径和选择其中最大的一个。

  1. 从根节点20开始,有以下几条可能的路径:
    • 20 -> -50 -> 30
    • 20 -> -50
    • 20 -> 15 -> 10
    • 20 -> 15 -> 8
    • 20 -> 15
  2. 计算每条路径的和:
    • 20 -> -50 -> 30:20 - 50 + 30 = 0
    • 20 -> -50:20 - 50 = -30
    • 20 -> 15 -> 10:20 + 15 + 10 = 45
    • 20 -> 15 -> 8:20 + 15 + 8 = 43
    • 20 -> 15:20 + 15 = 35
  3. 其中,路径和最大的路径是20 -> 15 -> 10,总和为45。

在这里插入图片描述


🏆下一篇:华为OD机试 - 简易内存池 - 逻辑分析(Java 2024 D卷 200分)

🏆本文收录于,华为OD机试(JAVA)真题(D卷+C卷+A卷+B卷)

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

在这里插入图片描述

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
华为OD机试-B代表团坐车Java实现如下: 可以使用Java中的对象和类来实现代表团坐车的功能。 首先,我们可以创建一个代表团类(DelegateGroup),用于存储代表团中每个成员的姓名、目的地和乘车状态等信息。在代表团类中可以定义成员变量name、destination和status等,并添加相应的getter和setter方法。 然后,我们可以创建一个乘车类(Bus),表示可以坐的公共交通工具。在乘车类中可以定义一个成员变量capacity,表示公共交通工具的总容量,以及一个成员变量occupied,表示当前已经被占用的座位数量。同样,还可以添加getter和setter方法。 接下来,在主程序中,我们可以实例化几个代表团对象,例如通过DelegateGroup类创建代表团成员A、B、C等,并为每个成员指定目的地和乘车状态等信息。 然后,我们可以实例化一个公共交通工具对象,例如通过Bus类创建一个公交车对象bus,并设置总容量和当前已被占用的座位数量等信息。 接着,我们可以通过循环遍历代表团成员,根据成员的乘车状态来判断是否可以为该成员配座位。如果成员的乘车状态为“需要乘车”且公共交通工具的座位尚未全部被占用,那么可以为该成员配一个座位,并将公共交通工具的已占用座位数量加1。如果成员的乘车状态为“不需要乘车”或公共交通工具的座位已全部占满,那么不为该成员配座位。 最后,我们可以输出每个代表团成员的姓名、目的地和乘车状态,并输出公共交通工具的总容量和已占用座位数量等信息。 以上就是用Java实现代表团坐车功能的简单示例。还可以根据实际需求进行扩展和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值