【算法】从数组中选取两个符合一定条件的数的算法与实例:华为OD机考双机位A卷 - 跳房子I

华为OD机考双机位A卷 - 跳房子I

题目描述

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

游戏参与者需要分多个回合按顺序跳到第1格直到房子的最后一格

跳房子的过程中,可以向前跳,也可以向后跳。

假设房子的总格数是count,小红每回合可能连续跳的步教都放在数组steps中,请问数组中是否有一种步数的组合,可以让小红两个回合跳到最后一格?

如果有,请输出索引和最小的步数组合。

注意:

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

  • 提供的数据保证存在满足题目要求的组合,且索引和最小的步数组合是唯一的。

输入描述

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

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

count ≤ 1000

0 ≤ steps.length ≤ 5000

-100000000 ≤ steps ≤ 100000000

输出描述

返回索引和最小的满足要求的步数组合(顺序保持steps中原有顺序)

题意理解

这道题目要求从一个给定的步数数组中找到一个步数组合,使得小红能够通过两次跳跃从第1格跳到第 count 格,并且这个组合在原数组中的索引和是最小的。输出是该步数组合中的两个步数,上顺序保持与 steps 数组中的顺序一致。

再说的明白一点,在 steps 数组中选两个数,使其之和等于 count ,并且这两个数在原数组中的索引和是最小的。


算法一:双循环遍历

这道题最容易想到的算法就是双循环遍历,这估计也是这道题被称为简单题的原因,时间复杂度是

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <sstream>
using namespace std;

int main() {
    string str;
    getline(cin, str);

    vector<int> steps;
    stringstream ss(str.substr(1, str.size() - 2));
    string token;
    while (getline(ss, token, ',')) {
        steps.push_back(stoi(token));
    }

    int count;
    cin >> count;

    // 初始化最小索引和为整数最大值,用于后续比较
    int min_index = INT_MAX;
    // 存储最优解的数对
    vector<int> bestResult;
    
    // 双循环遍历所有元素
    for (int i = 0; i < steps.size(); i++) {
        for (int j = i + 1; j < steps.size(); j++) {
            if (steps[i] + steps[j] == count) {
                // 如果当前数对的索引和小于已知的最小索引和
                if (i + j < min_index) {
                    min_index = i + j;
                    bestResult = {steps[i], steps[j]};
                    break;
                }
            }
        }
    }
    
    cout << "[" << bestResult[0] << ", " << bestResult[1] << "]" << endl;

    return 0;
}

算法二:哈希表法

这道题的题意就是已知一个数组,要我们在这个数组中寻找,使得:

我们可以对这个方程做一个简单的处理,得到:

从而,我们可以从数组第0个元素开始遍历,去寻找这个数组中,后面是否有等于的元素,如果有,使得,就这一对索引之和是否小于之前的最小值,如果是,就更新最小值。

这种方法的时间复杂度只需要。所以非常高效。

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <sstream>
#include <unordered_map>
using namespace std;

int main() {
    string str;
    getline(cin, str);

    vector<int> steps;
    stringstream ss(str.substr(1, str.size() - 2));
    string token;
    while (getline(ss, token, ',')) {
        steps.push_back(stoi(token));
    }

    int count;
    cin >> count;

    // 存储最优结果的数对
    pair<int, int> bestResult;
    int min_index = INT_MAX;
    // 创建哈希表,用于存储值到索引的映射
    unordered_map<int, int> value_to_index;
    
    // 遍历所有元素
    for (int i = 0; i < steps.size(); i++) {
        int target = count - steps[i];
        
        // 检查哈希表中是否存在互补值
        if (value_to_index.find(target) != value_to_index.end()) {
            // 获取互补值对应的索引
            int index = value_to_index[target];
            
            // 如果当前找到的数对索引之和小于已知的最小索引和
            if (i + index < min_index) {
                min_index = i + index;
                // 确保结果中的两个数按索引顺序排列(较小索引的数在前)
                if (i < index) {
                    bestResult = {steps[i], steps[index]};
                } else {
                    bestResult = {steps[index], steps[i]};
                }
            }
        }
        
        // 更新哈希表:
        // 1. 如果当前值不在哈希表中,或者
        // 2. 当前值已存在但当前索引更小(确保存储的是相同值中最小的索引)
        if (value_to_index.find(steps[i]) == value_to_index.end() || i < value_to_index[steps[i]]) {
            value_to_index[steps[i]] = i;
        }
    }

    cout << "[" << bestResult.first << "," << bestResult.second << "]" << endl;

    return 0;
}

算法三:回溯算法(DFS+剪枝+状态重置)

回溯算法的基本原理可以参考【算法】回溯算法的基本原理与实例:华为OD机考双机位A卷 - 乘坐保密电梯-CSDN博客

这道题我们可以把它看作是一个组合问题,也就是从N个数中找出K个数的组合问题,因此可以用回溯算法求解所有可能,找出下标只和最小的组合。

但问题是,回溯算法时间复杂度是,这个题中,即使限制了K的个数是2,其时间复杂度也是,所以效率很低,很大可能超过题目要求的运行时间。

虽然回溯算法并不是解这道题的高效算法,但是它是一种非常经典的算法,也需要理解。如果K的个数大于2,它的优势就会逐渐体现出来。

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <sstream>
using namespace std;

/**
 * @brief 深度优先搜索函数,用于寻找两个不同步数的最优组合
 * 
 * @param steps 存储每个可能的连续跳跃步数
 * @param count 目标总格子数
 * @param curResult 存储当前选中的索引组合(最多2个)
 * @param bestResult 引用参数,用于保存最优索引组合
 * @param visited 标记各步数是否已被访问的布尔向量
 * @param cur_pos 当前累计的步数总和
 * @param index_min 引用参数,用于保存当前找到的最小索引和
 */
void dfs(vector<int>& steps, const int count, vector<int>& curResult, vector<int>& bestResult, vector<bool>& visited, int& cur_pos, int& index_min) {
    // 递归终止条件:累计步数等于目标值且恰好选中了两个不同的步数
    if (cur_pos == count && curResult.size() == 2) {
        int index_temp = curResult[0] + curResult[1];
        // 如果当前索引和小于已知的最小索引和,则更新最优解
        if (index_temp < index_min) {
            index_min = index_temp;
            bestResult = curResult;
        }
        return;
    }

    // 遍历所有可能的步数
    for (int i = 0; i < steps.size(); i++) {
        // 剪枝条件:
        // 1. 当前步数已被访问
        // 2. 已经选了一个索引且当前索引小于已选索引(避免重复组合,如i=1,j=0和i=0,j=1视为同一组合)
        // 3. 已经选了两个或更多索引(题目只要求选两个)
        if (visited[i] || (curResult.size() == 1 && curResult[0] > i) || (curResult.size() >= 2)) {
            continue;
        }
        
        visited[i] = true;
        curResult.push_back(i);
        cur_pos += steps[i];
        
        // 递归搜索下一层
        dfs(steps, count, curResult, bestResult, visited, cur_pos, index_min);
        
        cur_pos -= steps[i];
        visited[i] = false;
        curResult.pop_back();
    }
}

int main() {
    string str;
    getline(cin, str);

    vector<int> steps;
    stringstream ss(str.substr(1, str.size() - 2));
    string token;
    while (getline(ss, token, ',')) {
        steps.push_back(stoi(token));
    }

    int count;
    cin >> count;

    vector<int> curResult;  // 存储当前选中的两个索引
    vector<int> bestResult; // 存储最优的两个索引
    vector<bool> visited(steps.size(), false);
    int cur_pos = 0;        // 当前位置的累加和
    int index_min = INT_MAX;// 最小索引和
    dfs(steps, count, curResult, bestResult, visited, cur_pos, index_min);

    if (bestResult.size() == 2) {
        cout << "[" << steps[bestResult[0]] << ", " << steps[bestResult[1]] << "]" << endl;
    }

    return 0;
}
相关推荐
老歌老听老掉牙1 天前
从战场到商场:最优化算法如何用数学重塑世界?
python·算法·最优化
im_AMBER1 天前
Leetcode 94 合并零之间的节点
数据结构·c++·笔记·学习·算法·leetcode
tobias.b1 天前
408真题解析-2009-3-数据结构-树-遍历方式
数据结构·计算机考研·408真题
KingRumn1 天前
DBUS源码剖析之DBusMessage消息头
linux·服务器·算法
WaWaJie_Ngen1 天前
【操作系统】第四章---存储器管理
数据结构·算法
sayang_shao1 天前
C++ 多线程【笔记】
c++
benben0441 天前
强化学习DQN和Actor-Critic算法
算法
爪哇部落算法小助手1 天前
每日两题day68
算法
编码小哥1 天前
OpenCV角点检测:Harris与ShiTomasi算法
人工智能·opencv·算法