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

有华为OD考试扣扣交流群可加:948025485
可上全网独家的 欧弟OJ系统 练习华子OD、大厂真题
绿色聊天软件戳 od1336了解算法冲刺训练

文章目录

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

题目描述与示例

题目描述

跳房子,也叫跳飞机,是一种世界性的儿童游戏。

游戏参与者需要分多个回合按顺序跳到第1格直到房子的最后一格,然后获得一次选房子的机会,直到所有房子都被选完,房子最多的人获胜。

跳房子的过程中,如果有踩线等违规行为会结束当前回合,甚至可能倒退几步。

假设房子的总格数是count,小红每回合可能连续跳的步数都放在数据steps中,请问数组中是否有一种步数的组合,可以让小红三个回合跳到最后一格?如果有,请输出索引和最小的步数组合,数据保证索引和最小的步数组合是唯一的。

注意:数组中的步数可以重复,但数组中的元素不能重复使用。

输入描述

第一行输入为房子总格数count,它是整数类型int

第二行输入为每回合可能连续跳过的步数,它是整数数组类型。

输出描述

返回索引和最小满足要求的步数组合。

注意:顺序保持steps中的原有顺序。

备注

  • count <= 10000;
  • 3 <= steps.length <= 10000;
  • -100000 <= steps[i] <= 100000;

示例一

输入

1,4,5,2,0,2
9

输出

4,5,0

说明

示例二

输入

1,5,2,0,2,4
9

输出

5,2,2

示例三

输入

-1,2,4,9
12

输出

-1,4,9

解题思路

注意,本题和LeetCode15、三数之和基本完全一致。区别仅仅在于本题需要找到下标和最小的索引。

代码

Python

# 题目:2023B-跳房子II
# 分值:200
# 作者:许老师-闭着眼睛学数理化
# 算法:相向双指针
# 代码看不懂的地方,请直接在群上提问


from math import inf

# 输入原数组lst
lst = list(map(int, input().split(",")))
# 输入目标和
target = int(input())

# 获得原数组长度n
n = len(lst)
# 对原nums数组进行排序,同时需要记录在原数组中的下标i,作为排序的第二个依据
nums = sorted((num, i) for i, num in enumerate(lst))

# 初始化一个长度为3的数组,用于储存三个数的下标,初始化均为inf
ans_idx = [inf, inf, inf]

# 考虑第一个数first
for i, (first, idx) in enumerate(nums[:-2]):
    # 如果发现第i、第i+1、第i+2个数的和加起来超过target
    # 则更靠后的的选择肯定和更大,直接退出循环
    if nums[i][0] + nums[i+1][0] + nums[i+2][0] > target:
        break
    # 去重操作,如果遇到连续两个数相等,那么考虑nums[i]和考虑nums[i-1]所作的计算是一样的
    # 直接跳过nums[i]的计算
    if i != 0 and nums[i][0] == nums[i-1][0]:
        continue
    # 构建相向双指针left和right
    left, right = i+1, n-1
    while left < right:
        # 计算三个数的和
        sum3 = first + nums[left][0] + nums[right][0]
        # 三数和太大,right左移
        if sum3 > target:
            right -= 1
        # 三数和太小,left右移
        elif sum3 < target:
            left += 1
        # 三数和等于target,如果三个下标和比之前记录过的三数下标和更小
        # 则更新ans_idx
        else:
            if sum(ans_idx) > idx + nums[left][1] + nums[right][1]:
                ans_idx = [idx, nums[left][1], nums[right][1]]
            right -= 1
            left += 1

# 退出循环,ans_idx为在原数组中的三个数的下标
# 答案需要按照下标大小进行排列
ans_idx.sort()
ans = ",".join(str(lst[idx]) for idx in ans_idx)
print(ans)

Java

import java.util.*;

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

        // 读取一行包含逗号分隔数字的数组
        String line = scanner.nextLine();

        // 使用 StringTokenizer 分割数字
        StringTokenizer tokenizer = new StringTokenizer(line, ",");
        List<Integer> lst = new ArrayList<>();
        while (tokenizer.hasMoreTokens()) {
            lst.add(Integer.parseInt(tokenizer.nextToken()));
        }

        // 读取下一行的数字
        int target = scanner.nextInt();

        // 获得原数组长度 n
        int n = lst.size();
        // 对原 nums 数组进行排序,同时需要记录在原数组中的下标 i,作为排序的第二个依据
        List<Pair> nums = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            nums.add(new Pair(lst.get(i), i));
        }
        Collections.sort(nums);

        // 初始化一个长度为 3 的数组,用于储存三个数的下标,初始化均为 n
        List<Integer> ansIdx = new ArrayList<>(Arrays.asList(n, n, n));

        // 考虑第一个数 first
        for (int i = 0; i < n - 2; i++) {
            // 如果发现第 i、第 i+1、第 i+2 个数的和加起来超过 target
            // 则更靠后的选择肯定和更大,直接退出循环
            if (nums.get(i).first + nums.get(i + 1).first + nums.get(i + 2).first > target) {
                break;
            }
            // 去重操作,如果遇到连续两个数相等,那么考虑 nums[i] 和考虑 nums[i-1] 所作的计算是一样的
            // 直接跳过 nums[i] 的计算
            if (i != 0 && nums.get(i).first == nums.get(i - 1).first) {
                continue;
            }
            // 构建相向双指针 left 和 right
            int left = i + 1, right = n - 1;
            while (left < right) {
                // 计算三个数的和
                int sum3 = nums.get(i).first + nums.get(left).first + nums.get(right).first;
                // 三数和太大,right 左移
                if (sum3 > target) {
                    right--;
                }
                // 三数和太小,left 右移
                else if (sum3 < target) {
                    left++;
                }
                // 三数和等于 target,如果三个下标和比之前记录过的三数下标和更小
                // 则更新 ansIdx
                else {
                    if (ansIdx.get(0) + ansIdx.get(1) + ansIdx.get(2) > nums.get(i).second + nums.get(left).second + nums.get(right).second) {
                        ansIdx = Arrays.asList(nums.get(i).second, nums.get(left).second, nums.get(right).second);
                    }
                    right--;
                    left++;
                }
            }
        }

        // 退出循环,ansIdx 为在原数组中的三个数的下标
        // 答案需要按照下标大小进行排列
        Collections.sort(ansIdx);
        for (int i = 0; i < 3; i++) {
            System.out.print(lst.get(ansIdx.get(i)));
            if (i < 2) {
                System.out.print(",");
            }
        }
        System.out.println();
    }

    static class Pair implements Comparable<Pair> {
        int first;
        int second;

        Pair(int first, int second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public int compareTo(Pair other) {
            return Integer.compare(this.first, other.first);
        }
    }
}

C++

#include 
#include 
#include 
#include 
#include 
using namespace std;

int main() {
    // 读取一行包含逗号分隔数字的数组
    string line;
    getline(cin, line);

    // 使用 stringstream 分割数字
    stringstream ss(line);
    vector<int> lst;
    int num;
    char comma;
    while (ss >> num >> comma) {
        lst.push_back(num);
    }

    // 读取下一行的数字
    int target;
    cin >> target;
    
    // 获得原数组长度 n
    int n = lst.size();
    // 对原 nums 数组进行排序,同时需要记录在原数组中的下标 i,作为排序的第二个依据
    vector<pair<int, int>> nums;
    for (int i = 0; i < n; i++) {
        nums.push_back({lst[i], i});
    }
    sort(nums.begin(), nums.end());

    // 初始化一个长度为 3 的数组,用于储存三个数的下标,初始化均为 n
    vector<int> ansIdx(3, n);

    // 考虑第一个数 first
    for (int i = 0; i < n - 2; i++) {
        // 如果发现第 i、第 i+1、第 i+2 个数的和加起来超过 target
        // 则更靠后的选择肯定和更大,直接退出循环
        if (nums[i].first + nums[i + 1].first + nums[i + 2].first > target) {
            break;
        }
        // 去重操作,如果遇到连续两个数相等,那么考虑 nums[i] 和考虑 nums[i-1] 所作的计算是一样的
        // 直接跳过 nums[i] 的计算
        if (i != 0 && nums[i].first == nums[i - 1].first) {
            continue;
        }
        // 构建相向双指针 left 和 right
        int left = i + 1, right = n - 1;
        while (left < right) {
            // 计算三个数的和
            int sum3 = nums[i].first + nums[left].first + nums[right].first;
            // 三数和太大,right 左移
            if (sum3 > target) {
                right--;
            }
            // 三数和太小,left 右移
            else if (sum3 < target) {
                left++;
            }
            // 三数和等于 target,如果三个下标和比之前记录过的三数下标和更小
            // 则更新 ansIdx
            else {
                if (accumulate(ansIdx.begin(), ansIdx.end(), 0) > nums[i].second + nums[left].second + nums[right].second) {
                    ansIdx = {nums[i].second, nums[left].second, nums[right].second};
                }
                right--;
                left++;
            }
        }
    }

    // 退出循环,ansIdx 为在原数组中的三个数的下标
    // 答案需要按照下标大小进行排列
    sort(ansIdx.begin(), ansIdx.end());
    for (int i = 0; i < 3; i++) {
        cout << lst[ansIdx[i]];
        if (i < 2) {
            cout << ",";
        }
    }
    cout << endl;

    return 0;
}

时空复杂度

时间复杂度:O(``n^2``)。双重循环所需时间复杂度。

空间复杂度:O(``n``)。列表nums所占空间。


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

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

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

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

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

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

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

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

你可能感兴趣的:(最新华为OD真题,#,双指针,java,c++,华为od,leetcode,python,算法)