动态规划与背包问题

https://blog.youkuaiyun.com/u013309870/article/details/75193592
这篇博客讲的非常好。

思想

其思想就是通过 记住求过的解来节省时间,在递归的算法中用特别爽。

菲波拉契算法

def fib0(n):
    if (n <= 0):
        return 0
    if (n == 1):
        return 1;
    return fib0(n-1) + fib0(n-2)

0 1 2 3 5 8 13 21…
算法的执行流程:
在这里插入图片描述
很多节点都被重复执行,浪费时间。
因此记住已经调用过的节点以便重复使用,可以大幅节约时间。

自顶向下的备忘录法

def fib1_c(n, Memo):
    if(Memo[n] != -1):
    //如果已经求出了fib(n)的值直接返回,否则将求出的值保存在Memo备忘录中。               
        return Memo[n]
    if(n <= 2):
        Memo[n] = 1
    else: Memo[n] = fib1_c(n-1, Memo) + fib1_c(n-2, Memo)
    return Memo[n]

def fib1(n):
    if (n<=0): 
        return 0;
    Memo = np.zeros(n+1)
    for i in range(0,n+1):
        Memo[i] = -1
    return fib1_c(n, Memo)

自底向上的动态规划

def fib2(n):
    if(n <= 0):
        return n;
    
    memo_0,memo_1 = 0, 1
    for i in range(2, n+1):
        memo = memo_0 + memo_1
        memo_0 = memo_1
        memo_1 = memo
        
    return memo

钢条切割

算法导论中的钢条切割问题:


def cut(p,n):
    if(n==0):
        return 0
    q = -1
    for i in range(1, n+1):
        q = max(q,p[i-1]+cut(p,n-i))
    return q;


def cut_c(p, n, r):
    q = -1
    if(r[n] >= 0):
        return r[n]
    if(n == 0): 
        q = 0
    else:
        for i in range(1, n+1):
            q = max(q, p[i-1] + cut_c(p, n-i, r))
    r[n] = q
    return q 

def cutMemo(p, n):
    r = np.zeros(len(p)+1) - 1
    return cut_c(p,n,r)


    
#自底向上的动态规划
def buttom_up_cut(p, n):
    r = np.zeros(n + 1)
    for i in range(1, n+1):
        q = -1
        for j in range(1,i+1):
            q = max(q,p[j-1] + r[i-j])
        r[i] = q
    return r[n]


p=[1,5,8,9,10,17,17,20,24,30]   
t00 = time.clock()
y4 = cut(p, 10)
t01 = time.clock()
t0 = t01 - t00
print(t0)
y5 = cutMemo(p, 5)
t10 = time.clock()
y6 = buttom_up_cut(p, 10)
t11 = time.clock()
t1 = t11 - t10
print(t1)

背包问题

在这里插入图片描述
这个例子讲的很清楚:
https://www.cnblogs.com/Christal-R/p/Dynamic_programming.html

动态规划最关键的就是填表,i表示你前i个物品,j表示背包的承重,(i,j)单元格表示在可选前i个物品在承重j的选择下,如何使价值最大。
在这里插入图片描述

def findMax(w, value, y): #动态规划 填表
    for i in range(1,len(w)): # 第i件物品
        for j in range(1,np.size(y,1)): #j所代表的是承重j的情况
            if(j < w[i]):   # 第i件装不进
                #y[i][j]代表前i件物品在承重j的情况下的最大价值
                y[i][j] = y[i-1][j] #y[1][1]=y[0][1]
            else:  # 能装
                #不装价值大
                if(y[i-1][j] > y[i-1][j-w[i]] + value[i]):  
                    y[i][j] = y[i-1][j]
                #装了价值大
                else:
                    y[i][j] = y[i-1][j-w[i]] + value[i]  
    #一直遍历到i=0结束为止,所有解的组成都会找到

然后是递归查找:

# 递归查找
def findWhat(i, j, w, value):  #寻找有i件可选的情况下承重为j的时候 解的组成方式
    if(i >= 0):
        if(y[i][j] == y[i-1][j]):
            item[i] = 0     # 标记物品未被选中
            findWhat(i-1, j, w, value)  #
        elif(j-w[i]>=0 and y[i][j] == y[i-1][j-w[i]] + value[i]):
            item[i] = 1     # 标记物品已被选中
            findWhat(i-1, j-w[i], w, value) #回到装第i个物品之前的位置
    return item

在上面的findmax程序中,记录下了整个表的情况,如果数据量太大,这会影响到内存开销,实际上我们的函数可以只记录我们想看的那一行,即只想知道在i个可选物品的情况下,j承重下的最大价值。

f ( i , j ) = m a x { f ( i − 1 , j ) , f ( i − 1 , j − w [ i ] ) + v a l u e ( i ) } f(i,j) = max\{ f(i-1,j), f(i-1, j - w[i]) + value(i)\} f(i,j)=max{f(i1,j),f(i1,jw[i])+value(i)}
可知,欲知道 f ( i , j ) f(i,j) f(i,j),只需知道 f ( i − 1 , : ) f(i-1,:) f(i1,:)这一样的价值.
在这里插入图片描述

# 利用状态转移公式简化空间,对j逆序访问,不然的话后面会被前面的已经修改的干扰
def findMaxBetter(w, value, y): 
    B = np.zeros([np.size(y,1)+1,1])
#    B = y
    for i in range(1,len(w) ):
        #j需要逆序,因为不然的话
        for j in range(np.size(y,1) ,0,-1):
            if(j-w[i]>=0 and B[j] <= B[j - w[i]] + value[i] ):
                B[j] = B[j - w[i]] + value[i]
                # B(j)= max{B(j), B(j-w(i))+v(i)};
                # 只考虑前i个物品的情况下的 不同的承重j对应的最优价值
    return B

使用动态规划的条件

原问题较难,但可拆分成一个一个的子问题,且子问题的解显而易见。在递归算法的基础上,将解决过的子问题记录下来来避免重复计算。
简而言之:最优子结构+重叠子问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值