华为OD机试真题---补种未成活胡杨


一、题目描述

近些年来,我国防沙治沙取得显著成果。某沙漠新种植N棵胡杨(编号1~N),排成一排。一个月后,有M棵胡杨未能成活。现可补种胡杨K棵,请问如何补种(只能补种,不能新种),可以得到最多的连续胡杨树?

  • 输入描述

    1. N:总种植数量,1<=N<=100000。
    2. M:未成活胡杨数量,1<=M<=N。M个空格分隔的数,按编号从小到大排列。
    3. K:最多可以补种的数量,0<=K<=M。
  • 输出描述:最多的连续胡杨棵树。

二、示例

  • 示例1

    • 输入:5 2 2 4 1(表示总共5棵树,2棵未成活,分别是2号和4号,最多补种1棵)。
    • 输出:3(补种到2或4结果一样,最多的连续胡杨棵树都是3)。
  • 示例2

    • 输入:10 3 2 4 7 1(表示总共10棵树,3棵未成活,分别是2号、4号和7号,最多补种2棵)。
    • 输出:6(补种第7棵树,最多连续胡杨树棵数为6,即5、6、7、8、9、10)。

三、解题思路

  1. 状态表示

    • 用0表示死树,1表示活着的树。
    • 将题目转化为求含有K个0的最长子串。
  2. 采用滑动窗口

    • 每当窗口含有K个0时,求子串最大长度。
    • 使用队列(Queue)存储每个0的下标,窗口有K个0时,将队列栈顶的下标更新为left。

四、解题步骤

  1. 读取输入数据,初始化相关变量。
  2. 创建一个长度为N+1的数组(为了方便处理,数组下标从1开始),初始化为全1(表示所有树都活着)。
  3. 根据输入的未成活胡杨编号,将数组中对应位置的元素置为0(表示死树)。
  4. 使用滑动窗口算法,遍历数组,记录窗口内0的数量,并动态调整窗口的大小。
  5. 当窗口内0的数量等于K时,记录当前窗口的大小(即连续活着的胡杨树的数量),并更新最大连续树木数量。
  6. 当窗口内0的数量超过K时,移动左指针缩小窗口,直到窗口内0的数量再次不超过K。
  7. 遍历结束后,输出最大连续树木数量。

五、代码实现(队列)


import java.util.Scanner;
import java.util.Queue;
import java.util.LinkedList;

public class PlantingTrees {

    /**
     * 找到可以补种得到最多的连续胡杨树的数量
     *
     * @param N 总种植数量
     * @param M 未成活胡杨数量
     * @param deadTrees 未成活胡杨的编号
     * @param K 最多可以补种的数量
     * @return 最多的连续胡杨树的数量
     */
    public static int maxContinuousTrees(int N, int M, int[] deadTrees, int K) {
        // 初始化数组
        int[] trees = new int[N + 1];
        for (int i = 1; i <= N; i++) {
            trees[i] = 1; // 所有树都活着
        }

        // 标记未成活的胡杨
        for (int tree : deadTrees) {
            trees[tree] = 0;
        }

        // 使用滑动窗口算法
        Queue<Integer> queue = new LinkedList<>();
        int left = 1, right = 1;
        int maxContinuous = 0;

        while (right <= N) {
            if (trees[right] == 0) {
                queue.offer(right);
            }

            while (queue.size() > K) {
                int deadIndex = queue.poll();
                left = deadIndex + 1;
            }

            maxContinuous = Math.max(maxContinuous, right - left + 1);
            right++;
        }

        return maxContinuous;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int M = scanner.nextInt();
        int[] deadTrees = new int[M];
        for (int i = 0; i < M; i++) {
            deadTrees[i] = scanner.nextInt();
        }
        int K = scanner.nextInt();
        scanner.close();

        int result = maxContinuousTrees(N, M, deadTrees, K);
        System.out.println(result);
    }
}


六、代码实现(数组)


import java.util.Scanner;  
  
public class PlantingTrees  {  
    public static void main(String[] args) {  
        Scanner scanner = new Scanner(System.in);  
  
        // 读取输入  
        int N = scanner.nextInt();  
        int M = scanner.nextInt();  
        int[] deadTrees = new int[M];  
        for (int i = 0; i < M; i++) {  
            deadTrees[i] = scanner.nextInt();  
        }  
        int K = scanner.nextInt();  
  
        // 初始化胡杨数组并标记死树  
        boolean[] treesAlive = new boolean[N + 1];  
        for (int i = 1; i <= N; i++) {  
            treesAlive[i] = true;  
        }  
        for (int deadTree : deadTrees) {  
            treesAlive[deadTree] = false;  
        }  
  
        // 滑动窗口算法  
        int left = 1, right = 1;  
        int zeros = 0; // 窗口内死树的数量  
        int maxLength = 0; // 最大连续活树的数量  
  
        while (right <= N) {  
            if (!treesAlive[right]) {  
                zeros++;  
            }  
  
            // 缩小窗口直到死树数量不超过K  
            while (zeros > K) {  
                if (!treesAlive[left]) {  
                    zeros--;  
                }  
                left++;  
            }  
  
            // 更新最大长度  
            maxLength = Math.max(maxLength, right - left + 1);  
  
            right++;  
        }  
  
        // 输出结果  
        System.out.println(maxLength);  
  
        scanner.close();  
    }  
}

运行示例解析

首先,让我们解析一下输入示例5 2 2 4 1

  • N = 5:总共有5棵胡杨。
  • M = 2:有2棵胡杨未成活。
  • deadTrees = [2, 4]:未成活胡杨的编号是2和4。
  • K = 1:最多可以补种1棵胡杨。

现在,让我们逐步分析代码的执行过程:

  1. 初始化trees数组,所有元素都设置为1(表示活着)。
  2. deadTrees数组中的值(2和4)在trees数组中对应的位置设置为0(表示未成活)。
  3. 使用滑动窗口算法遍历trees数组。

但是,代码中的滑动窗口算法有几个问题:

  • trees[right]为0时,将其索引添加到队列中。这是正确的。
  • 但是,当队列中的死树数量超过K时,代码只是简单地从队列中移除一个死树的索引,并将left设置为该索引的下一个位置。这是不正确的,因为这样做没有考虑到在leftright之间可能还有其他死树。此外,这样做可能会导致left跳过一些可能形成更长连续活树序列的位置。

正确的做法应该是:

  • 当队列中的死树数量超过K时,不断从队列中移除死树的索引,并递增left,直到队列中的死树数量不超过K为止。但是,left的递增应该基于队列中移除的死树索引的下一个位置(如果队列不为空),或者如果队列为空,则left可以直接设置为right(尽管在这种情况下,由于窗口内没有超过K个死树,所以通常不会发生这种情况)。
  • 然而,更简单且高效的方法是使用两个计数器而不是队列来跟踪窗口内0(死树)和1(活树)的数量,因为题目只关心死树的数量是否超过K
华为OD机试真题-学生重新排队是一个典的编程问题,下面是问题和解决路: 问题描述: 有n个学生站成一排,每个学生都有一个独一无二身份ID。现在给定一个初始的学生排列顺序,以及一系列的交换操作,交换操作表示将两个学生的位置进行交换。请你编写一个算法,输出最终的学生排列顺序。 解决思路: 这个问题可以使用数组来表示学生的排列顺序。首先,我们需要根据初始的学生排列顺序构建一个映射表,将每个学生的ID与其在数组中的位置对应起来。然后,我们按照给定的交换操作,更新映射表中学生的位置信息。最后,根据更新后的映射表,构建最终的学生排列顺序。 具体步骤如下: 1. 构建映射表:遍历初始的学生排列顺序,将每个学生的ID与其在数组中的位置对应起来,可以使用哈希表来实现。 2. 执行交换操作:按照给定的交换操作,更新映射表中学生的位置信息。 3. 构建最终的学生排列顺序:根据更新后的映射表,构建最终的学生排列顺序。 下面是一个示例代码,用于解决这个问题: ```python def rearrange_students(initial_order, swap_operations): # 构建映射表 mapping = {} for i, student_id in enumerate(initial_order): mapping[student_id] = i # 执行交换操作 for swap in swap_operations: student1, student2 = swap mapping[student1], mapping[student2] = mapping[student2], mapping[student1] # 构建最终的学生排列顺序 final_order = [0] * len(initial_order) for student_id, position in mapping.items(): final_order[position] = student_id return final_order ``` 使用上述代码,你可以通过传入初始的学生排列顺序和交换操作,得到最终的学生排列顺序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值