DP概念和编程方法

应用场景

DP是求解多阶段决策问题最优化的一种算法思想,它用于解决具有重叠子问题、具有子结构特征的问题。

能够解决的问题所具有的特征

重叠子问题、最优子结构。用DP可以高效率的处理具有这两个特征的问题。

1. 重叠子问题

        首先,子问题是原大问题的小版本,计算步骤一致;其次,计算大问题时,需要多次重复计算小问题。这就是重叠子问题。

        但是值得我们思考的是:在一些时候我们对一个同样的子问题会进行多次重复计算,耗费了大量的时间(参考斐波那契数列)。用DP处理重叠子问题时,每个子问题只计算一次,从而避免了重复计算,这就是DP效率高的原因。

        具体做法:首先分析得到最优子结构,然后递归或带记忆化搜索的递归进行编程,从而实现高效的计算。

        下面是递归法求解斐波那契数列:

int fib(int n){
    if(n==1||n==2) return 1;
    return fib(n-1)+fib(n+2);
}

        用纯递归的方法来解决问题的时间复杂度为O(2^n),可以看出非常浪费时间。

(DP代码往下看)

2. 最优子结构:

        在我们使用DP时,我们需要注重DP的一个性质就是“无后效性”。简单来说就是你现在做的事情与未来没有关系(参考斐波那契数列)。无后效性时DP的必要条件。因为只有这样我们才能降低算法的复杂度,应用DP才有意义。

DP的两种编程方法

        处理DP中的大问题和小问题。有两种思路:自顶向下(先大问题,再小问题);自底向上(先小问题,再大问题)。

1.自顶而下与记忆化

int memoize[N];
int fib(int n){
    if(n==1||n==2) return 1;
    if(memoize[n]!=0) return memoize[n];
    memoize[n]=fib(n-1)+fib(n-2);
    return memoize[n];
}

线性规划的时间复杂度为O(n)。相比上面的代码,时间复杂度得到了极大的改善。

2.自底而上与制表递推

这种方法与自顶而下相反,避免了递归编程。我们需要一个表(dp[])来维护我们的数据。

const int N = 255;
int dp[N];
int fib(int n){
    dp[1]=dp[2]=1;
    for(int i=3;i<=n;i++){
        dp[i]=dp[i-1]+dp[i-2];
    }
    return dp[n];
}

代码的时间复杂度也为O(n).

下节分享DP的设计和实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值