算法-贪心算法

# -*- coding: utf-8 -*-
"""
贪心算法
采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择,就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解。虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪心算法不要回溯

1. 定义:在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。
2. 特点:
    - 简单直观:通常直接且易于实现。
    - 效率:在很多情况下,贪心算法的运行时间较短。
    - 无记忆:每次选择都是独立的,不依赖于之前的状态。
    - 不总是得到最优解:在某些问题中,贪心选择可能导致非最优的全局解。
3. 基本思路:
    1. 把求解的问题分成若干个子问题;
    2. 对每个子问题进行求解,得到子问题的局部最优解;
    3. 把子问题的解(局部最优解)合成原来问题的解;

4. 应用于例子:①将数组和减半的最少操作次数 ②最大数 ③找零钱问题 ④买卖股票的最佳时机 ⑤仓库选址 ⑥分发糖果
"""
import copy

########## ①将数组和减半的最少操作次数
# 给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作)
# 请你返回将 nums 数组和 至少 减少一半的 最少 操作数。
# 输入:nums = [5,19,8,1]
# 输出:3
# 解释:初始 nums 的和为 5 + 19 + 8 + 1 = 33 。
# 以下是将数组和减少至少一半的一种方法:
# 选择数字 19 并减小为 9.5 。
# 选择数字 9.5 并减小为 4.75 。
# 选择数字 8 并减小为 4 。
# 最终数组为 [5, 4.75, 4, 1] ,和为 5 + 4.75 + 4 + 1 = 14.75 。
# nums 的和减小了 33 - 14.75 = 18.25 ,减小的部分超过了初始数组和的一半,18.25 >= 33/2 = 16.5 。
# 我们需要 3 个操作实现题目要求,所以返回 3 。
# 可以证明,无法通过少于 3 个操作使数组和减少至少一半。
"""
贪心策略:每次选出数组中的最大值,对该数减半,直到使数组和减小到一半。
"""


def minimum_operand(nums: list):
    count = 0
    or_sum = sum(nums)
    while sum(nums) >= or_sum / 2:
        max_value = max(nums)
        nums.remove(max_value)
        nums.append(max_value / 2)
        count += 1
    return count
# print(minimum_operand([5, 19, 8, 1]))
# print(minimum_operand([3, 8, 20]))

########## ②最大数
# 给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。
# 注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。
# 示例 1:

# 输入:nums = [10,2]
# 输出:"210"

# 示例 2:
#
# 输入:nums = [3,30,34,5,9]
# 输出:"9534330"
"""
贪心策略:先对原序列中的值取相同长度进行字符排序
"""


def max_value(nums: list):
    max_va = max(nums)
    nums_dict = {f"{value:0<{len(str(max_va))}}": str(value) for value in nums}
    nums_dict = dict(sorted(nums_dict.items(), key=lambda x: x[0], reverse=True))
    return "".join(nums_dict.values())
# print(max_value([10, 2]))
# print(max_value([3, 30, 34, 5, 9]))
# print(max_value([3, 30, 9, 5, 34]))

########## ③找零钱问题
# 对于人民币的面值有1元 、5元 、10元 、20元 、50元 、100元,下面要求设计一个程序,输入找零的钱,输出找钱方案中最少张数的方案
# 比如: 123元,最少是 1 张100 的,1 张 20 的,3 张 1 元的,一共5张。
"""
贪心策略: 选最大张,如果超过就选次大张
    
"""


def give_change(money: int):
    moneys = [100, 50, 20, 10, 5, 1]
    all = 0
    count = 0
    while all < money:
        for value in moneys:
            if all + value <= money:
                all += value
                count += 1
                break
    return count

# print(give_change(123))

########## ④买卖股票的最佳时机
# 给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
#
# 在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
#
# 返回 你能获得的 最大 利润 。
# 示例 1:
#
# 输入:prices = [7,1,5,3,6,4]
# 输出:7
# 解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
# 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3。
# 最大总利润为 4 + 3 = 7 。

# 示例 2:
#
# 输入:prices = [1,2,3,4,5]
# 输出:4
# 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
# 最大总利润为 4 。

# 示例 3:
#
# 输入:prices = [7,6,4,3,1]
# 输出:0
# 解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0。
"""
贪心策略:
只要明天比今天贵就今天买,明天卖
"""


def best_opportunity(nums: list):
    total = 0
    for i in range(len(nums) - 1):
        if nums[i + 1] > nums[i]:
            total += nums[i + 1] - nums[i]
    return total

# print(best_opportunity([7, 1, 5, 3, 6, 4]))
# print(best_opportunity([1, 2, 3, 4, 5]))
# print(best_opportunity([7, 6, 4, 3, 1]))

########## ⑤仓库选址
# 在一条数轴上有 N 家商店,它们的坐标分别为 ∼。
#
# 现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。
#
# 为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。
# 示例:
# 6 2 9 1
# 最小距离之和为12

"""
贪心策略:最远距离取中位数
"""


def storehouse_location(nums: list):
    nums_cp = copy.deepcopy(nums)
    while len(nums) > 1:
        nums.sort()
        middle = (nums.pop(0) + nums.pop(-1)) / 2
        nums.append(middle)
    distance = 0
    for value in nums_cp:
        distance += abs(value - nums[0])
    return distance
# print(storehouse_location([6, 2, 9, 1]))

########## ⑥分发糖果
# n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
#
# 你需要按照以下要求,给这些孩子分发糖果:
#
# 每个孩子至少分配到 1 个糖果。
# 相邻两个孩子评分更高的孩子会获得更多的糖果。
# 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
# 示例 1:
#
# 输入:ratings = [1,0,2]
# 输出:5
# 解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
# 示例 2:
#
# 输入:ratings = [1,2,2]
# 输出:4
# 解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
#      第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。
"""
贪心算法:每个相邻的大的比小的大一个
"""


def allocate_sugar(nums: list):
    sugar_list = [1] * len(nums)
    # 先从左往右
    for i in range(len(nums) - 1):
        max_va = max(sugar_list[i], sugar_list[i + 1])
        if nums[i] > nums[i + 1]:
            if sugar_list[i] <= sugar_list[i+1]:
                sugar_list[i] = max_va + 1
        elif nums[i] < nums[i + 1]:
            if sugar_list[i + 1] <= sugar_list[i]:
                sugar_list[i + 1] = max_va + 1
    # 再从右往左
    for i in range(len(nums) - 1, 0, -1):
        max_va = max(sugar_list[i], sugar_list[i - 1])
        if nums[i] > nums[i - 1]:
            if sugar_list[i] <= sugar_list[i - 1]:
                sugar_list[i] = max_va + 1
        elif nums[i] < nums[i - 1]:
            if sugar_list[i - 1] <= sugar_list[i]:
                sugar_list[i - 1] = max_va + 1

    return sum(sugar_list)


print(allocate_sugar([1, 0, 2]))
print(allocate_sugar([1, 2, 2]))
print(allocate_sugar([1, 3, 4]))
print(allocate_sugar([1, 2, 1, 2, 1]))
print(allocate_sugar([3, 4, 2]))
print(allocate_sugar([3, 4, 5]))
print(allocate_sugar([1, 2, 2, 5, 4, 3, 2]))

if __name__ == '__main__':
    pass

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值