贪心算法和动态规划

一. 贪心算法

1. 定义

贪心算法其实就是在面对问题求解的时候,选择最优的解决方案。也就是说不从整体加以考虑,他所做出的仅仅是在某种意义上得到局部最优(是否是全局最优还有待证明。)

2. 最优装载问题

运古董,每一件古董的固定重量是w , 总最大承载数是C, 求怎么样可以装走的古董价值最大?
这道题实际上就是求解最优解,即装走古董的总价值最大
思路:
1) 列古董的清单
2) 最大承载量C是一定的,每次选择体积最小的装到不能再装为止,这个就是贪心策略。
3) 从体积最小的中间选择价值最大的带走,使得总价值最大化
代码如下:

  # 定义每个古董价值
antique = [3, 10, 7, 11, 15, 5, 14, 2]


def max_ans(antique):
    anti_sort = sorted(antique)  # 对价值排序
    ans, t = 0, 0  # ans记录装载古董数量,t记录装载古董重量
    ship = []  # 记录装载的古董
    for a in anti_sort:
        t += a
        if t <= 30:
            ans += 1
            ship.append(a)
    print('装载古董数量:', ans)
    print('装载的古董', ship)


max_ans(antique)

运行结果为:
在这里插入图片描述

  1. 教室调用问题
    假如 有如下的课表,进可能将更多的课程安排在同一间教室上课,那么应该怎么安排?
    在这里插入图片描述

将课程表分化:
在这里插入图片描述
思路:
1) 选出最早结束的课程,该门课程就是在这间教室上的第一节课。
2) 选择第一节课结束之后才开始上的课。同样,选择最早结束的课程作为第二节课
3) 循环前两步

3. 背包问题

假设山洞中有n 件宝物,每种宝物有一定的重量w 和价值 v ,毛驴的运载能力是一种宝物只能拿一样,宝物可分割。那么怎么样毛驴运走的宝物价值最大?
有三种贪心策略:
1) 每次挑选价值最大的东西放在背包中
2) 每次挑选最重的东西
3) 每次选取单重量价值最大的东西
思路:
1) 计算出每件宝物的性价比,将性价比从高到低排序
2)按照性价比从大到小选取宝物, 直到达到毛驴的运载能力/每次选择宝物后判断是否小于M, 如果不小于则取走宝物的一部分,算法结束

实验代码如下:

# datas中每个元素代表一个古董,每个列表第一个元素代表古董重量,第二个元素代表古董价值
datas = [[4, 3], [2, 8], [9, 18], [5, 6], [5, 8], [8, 20], [5, 5], [4, 6], [5, 7], [5, 15]]
m = 30 # 毛驴运载能力
w = 0 # 获取的总价值
# 计算出每件宝物的性价比,按照从高到低排序
for i in range(len(datas)):
   price = datas[i][1] / datas[i][0]
   datas[i].append(price)  # 增加性价比
datas.sort(key=lambda data: data[2], reverse=True) # 按性价比排序
# 按性价比从大到小选取宝物,直到达到毛驴的运载能力
for data in datas:
   if data[0] <= m:
      w += data[1]
      m -= data[0]
   else:
      w += data[2] * m  # 取走宝物的一部分
      break
print('总价值:',w)

运行结果为:
在这里插入图片描述

二. 动态规划

1. 定义

动态规划是运筹学的一个分支,是求解决策过程最优解的数学方法。

2. 用动态规划Fib 数列

实例: 一共有n 阶楼梯,小孩一次可以上一阶或者是两阶,那么他走到n 阶楼梯,一共有多少种走法?

思路:
我们可以从后向前推导,如果想要走到第n 阶楼梯,那么有两种走法: 1)从n-1 爬一阶楼梯上到第n阶。 2)从n -2 阶爬2阶上到第n 阶楼梯。
所以 s[n] = s[n-1] + s[n-2]
实现代码如下:

def fib(n):
    s = [-1 for x in range(n + 1)]
    s[0] = 0
    s[1] = 1
    for i in range(2, n + 1):
        s[i] = s[i - 1] + s[i - 2]
    return s[n]


print(fib(10)) 
# 时间复杂度为O(n)

运行结果为:
在这里插入图片描述

3. 任务安排问题

各个任务的时长和收益都如图所示,求怎么样安排任务收益最大?
在这里插入图片描述
思路:
每个任务都有做或者不做两种选择,并且第二个任务必须在前一个任务完成之后才能开始。我们在考虑这个问题的时候从最后一个问题向前推导。
实验代码如下:

     # 数组arr存储的是每个任务的收益;
    arr = [0, 5, 1, 8, 4, 6, 3, 2, 4]
    # 数组prev存储的是指定任务之前可以执行的任务
    prev = [0, 0, 0, 0, 1, 0, 2, 3, 5]
    
    
    def dp_opt(arr):
        # 计算任务的长度
        len_arr = len(arr)
        # 存储执行到每个任务可以获得的最优解;
        opt = [0 for i in range(len_arr)]
        opt[0] = 0
        opt[1] = arr[1]
        for i in range(2, len(arr)):
            # 不选择做这个任务的最优解;
            A = opt[i - 1]
            # 选择做这个任务的最优解;
            B = arr[i] + opt[prev[i]]
            opt[i] = max(A, B)
        return opt[-1]
    
    
    print(dp_opt(arr))
               


运行结果如下: 

在这里插入图片描述

4. 不相邻数的最大和问题

问题描述: 给定数组A = [1,2,4,1,7,8,3], 求出数组A 中互不相邻地的数的最大和。
比如说: 选择了8 , 那么就不能选7 和 3.
实验代码如下:

# 数组arr存储的是每个任务的收益;
arr = [0, 5, 1, 8, 4, 6, 3, 2, 4]
# 数组prev存储的是指定任务之前可以执行的任务
prev = [0, 0, 0, 0, 1, 0, 2, 3, 5]


def dp_opt(arr):
    # 计算任务的长度
    len_arr = len(arr)
    # 存储执行到每个任务可以获得的最优解;
    opt = [0 for i in range(len_arr)]
    opt[0] = 0
    opt[1] = arr[1]
    for i in range(2, len(arr)):
        # 不选择做这个任务的最优解;
        A = opt[i - 1]
        # 选择做这个任务的最优解;
        B = arr[i] + opt[prev[i]]
        opt[i] = max(A, B)
    return opt[-1]


print(dp_opt(arr))

运行结果为:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值