浅谈动态规划DP

My thinking about dynamic programming

动态规划是一种以空间换时间的技巧,通常消耗的空间在接受范围内,但是速度却可以从指数
级下降到多项式的时间。在学习动态规划之前,需要先了解:

  1. Overlapping Subproblems
  2. Optimal Substructure Property

Steps to solve a DP

1) Identify if it is a DP problem

2) Decide a state expression with
least parameters
3) Formulate state relationship

4) Do tabulation (or add memoization)

下面我们就根据步骤来讲解:

Step 1 : How to classify a problem as a Dynamic Programming Problem?

  • 需要求最大最小值或者计数在某个条件下的个数
  • 满足最优子结构、重叠子问题和无后效性

Step 2 : Deciding the state

DP问题的核心就是状态和状态的转移。状态的定义将会决定状态转移的方式。所以,下面来看一下如何来定义DP
的状态吧。

状态简单地说就是参数,用来确定子问题的参数。参数的个数最好尽量的少,这样可以减少状态空间。

例如,在我们的背包问题中,我们通过下标和重量来定义状态:DP[index][weight].
这里DP[index][weight]告诉我们可以获得的最大价值(value),我们可以通过index和weight
的变化来写出转移方程,即确定了子问题。

正如我们所知,DP是通过已经计算的结果来得到最终的结果的方法。所以,下面我们就来介绍如何寻找
previous state 和 current state 的关系

Step 3 : Formulating a relation among the states

这部分应该是DP中最难的一部分了,而且需要练习和观察。下面给出一个例子:

Given 3 numbers {1, 3, 5}, we need to tell
the total number of ways we can form a number 'N' 
using the sum of the given three numbers.
(allowing repetitions and different arrangements).

Total number of ways to form 6 is: 8
1+1+1+1+1+1
1+1+1+3
1+1+3+1
1+3+1+1
3+1+1+1
3+3
1+5
5+1

首先我们先定义状态,我们可以定义state(n):the total number of arrangements to form n by using {1, 3, 5} as elements.

所以我们的最终目标是计算state(n).

下面我们来展示如何得到state(7):

1) Adding 1 to all possible combinations of state (n = 6)

Eg : [ (1+1+1+1+1+1) + 1]

[ (1+1+1+3) + 1]

[ (1+1+3+1) + 1]

[ (1+3+1+1) + 1]

[ (3+1+1+1) + 1]

[ (3+3) + 1]

[ (1+5) + 1]

[ (5+1) + 1]

2) Adding 3 to all possible combinations of state (n = 4);

Eg : [(1+1+1+1) + 3]

[(1+3) + 3]

[(3+1) + 3]

3) Adding 5 to all possible combinations of state(n = 2)

Eg : [ (1+1) + 5]

通过细心的思考和发现,我们可以得到:

state(7)=state(6)+state(4)+state(2)

或者说,

state(7)=state(7-1)+state(7-3)+state(7-5)

再把它推广一下:

state(n)=state(n-1)+state(n-3)+state(n-5)

所以我们暂且可以写出如下的代码:

int solve(int n)
{ 
   // base case
   if (n < 0) 
      return 0;
   if (n == 0)  
      return 1;  

   return solve(n-1) + solve(n-3) + solve(n-5);
} 

但是,这份代码的时间是指数级的,因为存在大量的重复计算,所以下一步就是需要储存已经计算的结果。

Step 4 : Adding memoization or tabulation for the state

我们只需要把计算过的结果保存在数组或者哈希表中(较少情况下,超大背包的时候也许有用)。
所以把上面的那份代码加一个记忆化储存结果就可以了:

// initialize to -1
int dp[MAXN];

// this function returns the number of 
// arrangements to form 'n' 
int solve(int n)
{ 
  // base case
  if (n < 0)  
      return 0;
  if (n == 0)  
      return 1;

  // checking if already calculated
  if (dp[n]!=-1) 
      return dp[n];

  // storing the result and returning
  return dp[n] = solve(n-1) + solve(n-3) + solve(n-5);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值