关于动态规划

Dynamic Programming

(1)我们首先看一下Fibnacci Sequence(斐波那契数列)
1 1 2 3 5 8 13 21 …
用数学公式可以定义为:
f ( x ) = { 1 x=0,1 f i b ( n − 1 ) + f i b ( n − 2 ) x!=0,1 f(x)= \begin{cases} 1& \text{x=0,1}\\ fib(n-1) + fib(n-2)& \text{x!=0,1} \end{cases} f(x)={1fib(n1)+fib(n2)x=0,1x!=0,1

从某一种程度上而言,上述的斐波那契数列问题是一个递归问题,同时也是一个重叠子问题,关于递归问题,我们显而易见就可以看出来,但是如何是一个重叠子问题(overlap sub-problem)呢?接下来,继续看:
假设我们求fib(6),如下图所示:
1
我们要明白的是,让计算机去求解一个fib(6),它只会像上图所示的从上至下进行相关的计算,看我用浅绿颜色标记出来的两个fib(4),在计算机运行中,就进行了相应的重复计算,这个算法的时间复杂度是 O ( 2 n ) O(2^{n}) O(2n),这也就是我们上述所说的重叠子问题(overlap subproblem),那如何去解决这个重叠子问题呢?这就会体现动态规划的思想了。
我们首先看一下以下的计算方式:
同样是求fib(6)
我们先求fib(1),继而求fib(2)…然后求fib(6),每一次求前面的一个fib的时候,在内存里记录一下,然后当求下一个fib的时候,可以从内存里取出上一个fib的值,这样的话,就避免了重复计算的问题,而且这样的话,解决此问题的时间复杂度就是 O ( n ) O(n) O(n)
(2),我们接下来看一个经典的类似于背包问题的问题
问题的描述是这样的:一个员工去干小时工,小时工的时间以及赋予他的工资如下图所示:2
其中横轴表示的是时间,$nums,表示第几个work会收到nums回报。 问题是,这个人该如何选择工作,可以得到最大力度的回报。
好,我们接下来,来计算这个问题,首先,针对每一个work我们都有两种选择,选或者是不选,我们把最优解表示为opt(i),这个公式表示前i个的最优解。value(i)表示第i个的利润。prev(i)表示第i个work的前面所能做的work。
首先,我们先列出一个表,估计就可以看懂我说了什么个意思了。

iprev(i)opt(i)
10
20
30
41
50
62
73
85

先忽略opt(I)。我们接下来看opt(i),这个函数:
o p t ( i ) = m a x { 选 value(i) + opt(prev(i)) 不 选 opt(i-1) opt(i)=max \begin{cases} 选& \text{value(i) + opt(prev(i))}\\ 不选 & \text{opt(i-1)} \end{cases} opt(i)=max{value(i) + opt(prev(i))opt(i-1)

3

然后,我们再回溯上面表格的opt(i)

iprev(i)opt(i)解释
105max {(value(1)) + opt(prev(1)) , opt(0)}
205max {(value(2)) + opt(prev(2)) , opt(1)}
308max {(value(3)) + opt(prev(3)) , opt(2)}
419max {(value(4)) + opt(prev(4)) , opt(3)}
509max {(value(5)) + opt(prev(5)) , opt(4)}
629max {(value(6)) + opt(prev(6)) , opt(5)}
7310max {(value(7)) + opt(prev(7)) , opt(6)}
8513max {(value(8)) + opt(prev(8)) , opt(7)}

(3),ok,看到这里,估计对动态规划就有了一个简单的了解,接下来我们用编程的方式去实现一个动态规划问题,就会更加加深对动态规划问题的理解。(python实现)
存在一个数组:arr = [1,2,4,1,7,8,3],问题是这样的,在这个数组中选择不相邻的数字,使其sum和最大。
首先,我们递归实现:
写递归程序重要的一点就是找到递归出口:
这个问题的递归出口如下:

{ r e c − o p t ( 0 ) = a r r [ 0 ] r e c − o p t ( 1 ) = m a x ( a r r [ 0 ] , a r r [ 1 ] ) \begin{cases} rec-opt(0) = arr[0]\\ rec-opt(1) = max (arr[0],arr[1]) \end{cases} {recopt(0)=arr[0]recopt(1)=max(arr[0],arr[1])

arr = [1,2,4,1,7,8,3]
# i :length of arr
def rec_opt(arr,i):
	if i==0:
		return arr[0]
	if i==1:
		return max(arr[0],arr[1])
	else:
		A = rec_opt(arr,i-2) + arr[i]
		B = rec_opt(arr,i-1)
		return max(A,B)

然后,我们采用非递归的方式,也就是动态规划的思想,去用python实现这个问题:

def dp_opt(arr,i):
	opt = np.zeros(len(arr))
	opt[0] = arr[0]
	opt[1] = max (arr[0],arr[1])
	for i in range(2,len(arr)):
		A = opt[i-2] + arr[i]
		B = opt[i-1]
		opt[i] = max(A,B)
	return opt[len(arr) -1s] 

(4),上述是一个一维空间上的动态规划,相对而言比较容易,下面我们看一下二维空间下的动态规划问题。这个问题是最近朋友面试的时候所遇到的一个问题,也是十分经典的一个问题,金字塔数字问题:(c++ 实现)
递归

#include<cstdio>
#include<algorithm>

using namespace std;
int n,a[1003][1003] = {0};
bool b[1003][1003];
int opt(int i,int j){
    if(i == n)
        return a[n][j];
    if(!b[i][j]){
        a[i][j] = max(opt(i+1,j),opt(i+1,j+1)) + a[i][j];
        b[i][j] = true;
    }
    return a[i][j];
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j =1;j<=i;j++){
            scanf("%d",&a[i][j]);
        }
    }
    printf("%d",opt(1,1));
}

非递归(动态规划)

/*
[[0,0,0,0,7,0,0,0,0],
 [0,0,0,3,0,8,0,0,0],
 [0,0,8,0,1,0,1,0,0],
 [0,2,0,7,0,4,0,0,0],
 [4,0,5,0,2,0,6,0,5]]
 */

 #include<cstdio>
 #include<algorithm>
 using namespace std;
 int r,a[1002][1002],F[1002][1002];
 int main(){
    scanf("%d",&r);
    for(int i = 1;i<=r;i++)
        for(int j =1;j<=i;j++){
            scanf("%d",&a[i][j]);
            F[i][j] = a[i][j];
        }
    for(int i =r-1;i>0;i--)
        for(int j=1;j<=i;j++){
            F[i][j]+=max(F[i+1][j],F[i+1][j+1]);
        }
    printf("%d",F[1][1]);


 }

(5),接下来,我们再看一个迷宫问题

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值