# -*- 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
01-14
1147

08-29
6937

12-15
4674

08-09