华为OD机试真题C卷

文章目录

  • 田忌赛马
  • 抢七游戏
  • 密码解密
  • 攀登者1
  • 攀登者2

田忌赛马

  • 输入a、b是两个整数数组,调整a中数字的顺序,使得尽可能多的a[i] > b[i];两数组长度相同且小于等于10;
  • 数组a、b中的数字各不相同(无重复数字);
  • 输出可以达到最优结果的a数组(组合)的数量;

示例1
输入:
11 8 20
10 13 7
输出:
1

示例2
输入:
11 12 20
10 13 7
输出:
2

思路(暴力求解):

  • 获取数组a的所有的组合情况,即全排列;
  • 遍历数组a的每一种组合,计算a[i] > b[i] 的数值个数,计为count;
  • 使用advantage_list 表示每种a组合中(a[i] > b[i])目标数值个数;
    • 索引 0 表示a组合中没有a[i] > b[i] 的数值;
    • 索引 1 表示a组合中只有一个a[i] > b[i] 的数值;
    • 索引 a_length 表示a组合中所有元素均满足a[i] > b[i] ;
    • advantage_list中索引越大,表示结果越优,输出最大索引的非零值
# __author__ = "laufing"
# 数组a 全排列
from typing import List


class HorseRacing:
    def solution(self, all_list: List[List[int]], a: List[int], b: List[int]) -> None:
        """
        :param all_list: 数组a的全排列
        :param a: 整数数组a
        :param b: 整数数组b
        :return:
        """
        # 最优情况列表,数组a对应索引i位置 a[i] > b[i] 的数字个数
        # 可为0个 1个 ... len(a)个
        a_length = len(a)
        advantage_list = [0] * (a_length + 1)

        # 依次遍历数组a的全排列all_list, 统计最优情况
        for alist in all_list:
            count = 0
            for i in range(a_length):
                if alist[i] > b[i]:
                    count += 1
            # 数组a的当前排列对应的最优情况 +1
            advantage_list[count] += 1

        # advantage_list 的索引越大,表示结果越优
        for k in advantage_list[::-1]:
            if k:
                print(k)
                break

    def get_all_list(self, a: List[int]) -> List[List[int]]:
        """
        递归求全排列
        :param a: 整数数组
        :return: a的全排列
        """
        result = []
        if len(a) == 1:
            result.append(a)
            return result

        for i in a:
            # 遍历数组a中的每一个值,作为组合开头, 剩余元素全排列
            temp = a.copy()
            temp.remove(i)
            # 全排列剩余元素
            all_list = self.get_all_list(temp)
            # 剩余元素的全排列 开头加入 i
            for per_list in all_list:
                per_list.insert(0, i)
            result.extend(all_list)

        return result


if __name__ == '__main__':
    horse_racing = HorseRacing()
    while True:
        try:
            array_a = list(map(int, input("alist:").split()))
            array_b = list(map(int, input("blist:").split()))

            # 获取数组a的全排列
            all_list = horse_racing.get_all_list(array_a)
            print(len(all_list), all_list)
            horse_racing.solution(all_list, array_a, array_b)
        except KeyboardInterrupt:
            break

(暴力)深度优先的求解:

# __author__ = "laufing"
# 暴力求解

import functools
import sys
import copy
import re
import math


def update(array_b, a_length):
    global advantage_map, result
    count = 0
    for i in range(a_length):
        if (result[i] > array_b[i]):
            count += 1
    advantage_map[count] += 1


def dfs(current_len, array_a, array_b, a_length):
    """
    深度优先遍历
    :param current_len: 当前长度
    :param array_a: 数组A
    :param array_b: 数组B
    :param a_length: 数组A的长度
    :return:
    """
    # 声明全局变量
    global advantage_map, result, visited

    if current_len == a_length:
        update(array_b, a_length)
    else:
        i = 0
        while True:
            if i >= a_length:
                break
            else:

                # visited中值为0 且 数组a中的当前值与前一个不重复
                if visited[i] == 0 and (not (i > 0 and array_a[i] == array_a[i - 1])):
                    visited[i] = 1
                    result[current_len] = array_a[i]
                    dfs(current_len + 1, array_a, array_b, a_length) # 最终更新adv 并返回
                    visited[i] = 0
            i += 1


if __name__ == '__main__':

    advantage_map = []
    visited = []
    result = []
    while True:
        try:
            array_a = list(map(int, input("alist:").split()))
            array_b = list(map(int, input("blist:").split()))

            # 数组A  升序排序
            array_a.sort()
            a_length = len(array_a)
            for i in range(a_length):
                visited.append(0)
                result.append(0)
                advantage_map.append(0)
            advantage_map.append(0)
            
            dfs(0, array_a, array_b, a_length)

            # 最优映射
            print(advantage_map)

            # 选择次数
            output = 0
            count = 0
            for i in range(a_length + 1):
                if (advantage_map[i] != 0):
                    output = advantage_map[i]
            print(output)
            
        except KeyboardInterrupt:
            break

 

抢七游戏

  • A、B两个人玩抢7游戏,A先报一个数字X [10, 10000], B报下一个数字Y,Y
  • A再报下一个数字Z,Z
  • 抢到7的获胜,在B赢的情况下,一共有多少种组合?

输入:
起始数字,在【10, 10000】之间
输出:
B赢的组合数

示例1:
输入:
10
输出:
1

示例2:
输入:
15
输出:
17

思路:

  • 在上一个人报数后,每个人有两种报数方式
    • 上一个数-1;
    • 上一个数-2;
  • 依次报数构建出一棵二叉树(递归问题),每层都分类为A/B;
  • 直到抢到7,或者报出的数小于7 结束;
# __author__ = "laufing"


class SaySeven:
    def solution(self, num, flag=0, init=True):
        """
        :param num: int, A报数的起始数字
        :param flag: 0 表示A报数, 1 表示B报数
        :param init: bool 是否起始报数
        :return: int,B赢的组合数
        """
        if init:
            self.count = 0

        if num <= 6:
            # 没有人赢
            return self.count

        elif num == 7:
            if flag == 1:
                # B赢的情况
                self.count += 1
            return

        flag ^= 1
        self.solution(num - 1, flag, init=False)
        self.solution(num - 2, flag, init=False)

        return self.count


if __name__ == '__main__':
    say_seven = SaySeven()
    while True:
        try:
            m = int(input("起始值:").strip())
            result = say_seven.solution(m)
            print(result)
        except KeyboardInterrupt:
            break

 

密码解密

  • 给一段‘加密’的字符串s,其中每个字符都是经过加密规则映射的;
  • 加密规则一对一映射:
    • a ~ i --> 1 ~ 9
    • j ~ z --> 10* ~ 26*
  • 将该密文解密输出

示例1
输入:
201920*
输出:
tst

示例2
输入:
129
输出:
abi

示例3
输入:
1201920*
输出:
atst

示例3
输入:
1021768
输出:
jugfh

思路:

  • 密文中是否有"*"
    • 无,则每个字符通过借助ASCII码映射到a ~ i之间的一个字符;
    • 有,密文字符串以"*"进行分割,并去除最后一个空字符;然后依次遍历得到的列表,拿到每个列表元素都判断长度是否大于2:
      • 大于2,则将最后两位作为一个整体解析(按照10* ~ 26规则反向映射),前面的元素作为无""情况解析映射;
      • 等于2,则将整体按照10* ~ 26*规则反向映射;
  • 解密的结果累计拼接到result 空字符串
# __author__ = "laufing"


class Decrypt:
    def solution(self, encrypt_s):
        result = ""
        if "*" not in encrypt_s:
            result += self.simple_parse(encrypt_s)
            return result

        alist = encrypt_s.split("*")
        if not alist[-1]:
            result += self.complex_parse(alist[:-1])
        else:
            result += self.complex_parse(alist[:-1]) + self.simple_parse(alist[-1])
        return result

    def simple_parse(self, enc_s):
        result = ""
        for i in enc_s:
            result += chr(int(i) + 96)
        return result

    def complex_parse(self, alist):
        result = ""
        for e in alist:
            if len(e) > 2:
                result += self.simple_parse(e[:-2]) + chr(int(e[-2:]) + 96)
            else:
                result += chr(int(e[-2:]) + 96)

        return result


if __name__ == '__main__':
    decrypt = Decrypt()
    while True:
        try:
            encrypt_s = input("s:").strip()
            result = decrypt.solution(encrypt_s)
            print(result)
        except KeyboardInterrupt:
            break

 

攀登者1

  • 攀登者使用的地图是一个一维数组,索引表示水平位置,数组元素表示海拔高度,数组元素0表示水平面;
  • 数组[0, 1, 2, 4, 3, 1, 0, 0, 1, 2, 3, 1, 2, 1, 0] 代表如下地图,有两个山脉位置(索引)1,2,3,4,5和8,9,10,11,12,13,最高山峰分别为4,3 , 对应的位置为3,10;
  • 一个山脉可能有多座山峰(高度大于相邻位置或者在地图边界且高度大于相邻高度)
    华为OD机试真题C卷_第1张图片

攀登者想要知道一张地图中有多少座山峰不是最高峰
输入描述:
输入一个逗号分隔的整数字符串,数值个数>=2
输出描述:
地图中山峰的数量

示例1
输入:
0,1,4,3,1,0,0,1,2,3,1,2,1,0
输出:
3

示例2
输入:
3,2,4,5,3,2,1,0,7
输出:
3

思路:

  • heights数组存储True/False,若海拔升高,则对应索引位置为True,否则为False;
  • 依次遍历海拔数组,第一个值与 landlevel=0 地平线比较,若大于地平线,则heights追加True,否则追加False,并且landlevel重新赋为当前海拔值;继续遍历比较;
  • 然后遍历heights ,索引从0 -> n-1 ,若发现True->False相邻,则result += 1;
  • 判断heights中最后一个是否为True,为True则result += 1;
  • 返回result
# __author__ = "laufing"
from typing import List


class Climber:
    def solution(self, arr: List[int]) -> int:
        # 存储山峰个数
        result = 0

        # 初始值
        land_level = 0
        # 海拔升高为True,降低为False
        heights = []
        for idx, val in enumerate(arr):
            if val > land_level: #
                land_level = val
                heights.append(True)
            else:
                land_level = val
                heights.append(False)

        # 统计True->False的组合数
        for i in range(0, len(heights) - 1):
            if heights[i] and not heights[i+1]:
                result += 1

        # 判断最后一位边界
        if heights[-1]:
            result += 1

        return result


if __name__ == '__main__':
    climber = Climber()

    while True:
        try:
            arr = list(map(int, input("vals:").strip().split(",")))
            result = climber.solution(arr)
            print(result)
        except KeyboardInterrupt:
            break

 

攀登者2

  • 攀登者1的基础上,攀登者会消耗体力(整数);
  • 上山时,消耗相邻高度差两倍的体力,下山时,消耗相邻高度差一倍的体力,平地不消耗体力,体力消耗到0时有生命危险;
  • 攀登者体力上限为999;
  • 登山的起点、终点可以是地图中的任何海拔为0的地面;

输入描述:
输入地图数组,体力整数值
输出描述:
地图内有多少个山峰可以攀登且安全返回地面;

示例1
输入:
[0,1,2,4,3,1,0,0,1,2,3,1,2,1,0],13
输出:
3
说明:
攀登路径为0->3->0 消耗体力12
攀登路径为7->10->7 消耗体力9
攀登路径为14->12->14 消耗体力6
所以有三座 可以攀登且安全返回地面的山峰;

示例2
输入:
[1,4,3],999
输出:
0
说明: 没有海拔为0的地平面,无法攀登。

思路:

  • 找到所有的山峰索引位置,放入mountain_top 列表;
  • 找到所有的地平面0位置,放入landlevel列表;
  • 为每个山峰寻找最合适的起始点,计算基于起始点组合的最小体力消耗,若小于攀登者的体力,则result += 1 (初始为0)
# __author__ = "laufing"
from typing import List


class Climber:
    def solution(self, arr: List[int], energy: int):
        result = 0

        # 如果没有地平面0,则无法攀登
        if 0 not in arr:
            return result

        # 有地平面0, 判断可以安全攀登的山峰数
        # 找出所有的山峰 所在索引位置
        mountain_top = self.get_mountain_top(arr)
        print("找到的山峰位置:", mountain_top)

        # 找出所有的地平面 所在的索引位置
        land_level = self.get_land_level(arr)
        print("找到的地平面:", land_level)

        # 计算每个山峰的最小 体力消耗
        while mountain_top:
            cur_top = mountain_top.pop(0)
            start = None
            end = None
            for idx in range(len(land_level)-1):
                if cur_top > land_level[idx] and cur_top < land_level[idx+1]:
                    start = land_level[idx]
                    end = land_level[idx+1]
            # 左边界的山峰
            if start is None and cur_top < land_level[0]:
                start = end = land_level[0]
            # 右边界的山峰
            if start is None and cur_top > land_level[-1]:
                start = end = land_level[-1]

            print("选择起始点:", (cur_top, start, end))

            # 选择好起始点后, 计算基于该起始点的最小体力消耗
            # pending  上山 消耗差距的2倍   下山消耗差距的1倍  平路不消耗
            min_enery = self.get_min_energy(arr, cur_top, start, end)
            if min_enery < energy:
                result += 1
        return result

    def get_min_energy(self, arr, cur_top, start, end):
        """
        :param arr: 海拔整数数组
        :param cur_top:  当前山峰索引位置
        :param start: 起点
        :param end: 终点
        :return:
        """
        min_energy = 999
        # 边界山峰,只能从一边上、下
        if start == end:
            # 待实现........
            pass
        else:
            # 山峰在中间
            start_to_start = 0
            deltas = []
            for i in range(start + 1, cur_top+1): # 索引位置
                delta = arr[i] - arr[i-1]
                deltas.append(delta)
                start_to_start += delta * 2
            start_to_start += sum(deltas)

            start_to_end = 0
            for i in range(start + 1, end + 1):
                if i <= cur_top:
                    # 上山
                    start_to_end += (arr[i] - arr[i-1]) * 2
                else:
                    start_to_end += abs(arr[i] - arr[i-1])

            end_to_end = 0
            deltas2 = []
            for i in range(end - 1, cur_top - 1, -1):
                delta = arr[i] - arr[i+1]
                deltas2.append(delta)
                end_to_end += delta * 2
            end_to_end += sum(deltas2)

            end_to_start = 0
            for i in range(end - 1, start - 1, -1):
                if i >= cur_top:
                    # 上山
                    end_to_start += (arr[i] - arr[i+1]) * 2
                else:
                    # 下山
                    end_to_start += abs(arr[i] - arr[i+1])

            min_energy = min([min_energy, start_to_start, start_to_end, end_to_start, end_to_end])

        return min_energy

    def get_mountain_top(self, arr):
        """
        :param arr: 海拔 整数数组
        :return: 山峰索引位置数组
        """
        # 存储山峰的索引为值
        mountain_top = []

        # 找山峰
        heights = []
        landlevel = 0
        for idx, val in enumerate(arr):
            if val > landlevel:
                heights.append(True)
                landlevel = val
            else:
                heights.append(False)
                landlevel = val

        for i in range(len(heights) - 1):
            if heights[i] and not heights[i+1]:
                mountain_top.append(i)

        if heights[-1]: # 判断最后一个边界是否为山峰
            mountain_top.append(len(arr)-1)

        return mountain_top

    def get_land_level(self, arr):
        """
        :param arr:   海拔 整数数组
        :return: 地平面0的数组
        """
        land_level = []
        for idx, val in enumerate(arr):
            if val == 0:
                land_level.append(idx)

        return land_level


if __name__ == '__main__':
    climber = Climber()

    while True:
        try:
            arr_string, energy = input("vals:").strip().rsplit(",", maxsplit=1)
            arr = eval(arr_string) # 恢复列表
            energy = int(energy)  # 转为整数

            result = climber.solution(arr, energy)
            print(result)
        except KeyboardInterrupt:
            break

你可能感兴趣的:(算法与数据结构(python),华为od,算法)