DP的理解及阶梯案例、三色组合案例

个人对DP的本质理解:一种解决问题的思路。当问题无法显示的用公式表达出来或者表达出来非常复杂,就可以想到用动态规划。

用数据公式可以表示,最简单的菲波拉契计算如下所示:

f(n) = f(n-1) + f(n-2)

可以形象理解成如何将N的求解与N-1、N-2等关联起来,然后用递归的方式计算出答案。

比如说爬阶梯的问题:

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)

这里要采用逆向思维解决问题,因为正向的解决是显式解决,需要计算出一个公式,这里实现不了。
第n个台阶可以是n-1跳一级台阶上来的,也可以是n-2跳二个台阶上来的。所以
f(n) = f(n-1) + f(n-2)
f(1) = 1
f(2) = 2

class Solution:
    def climb_stairs(self, n):
        """
        :type n: int
        :rtype: int
        """
        dp = [1] * n  #状态的定义
        for i in range(2,n+1):
            dp[i] = dp[i-1]+dp[i-2]	#状态转移方程
        return dp[n]

三色排列问题的解决方案:

有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.

假设有5个格子。ABCDE
错误的方法、
A可以填写3种颜色,B不与A相同可以填写2种颜色,C不与B相同可以填写2种颜色,
D不与C相同可以填写2种颜色,E不与A和D相同只可以填写1种颜色。
所以3*2*2*2*2*1,抽象的公式 $3*2^{n-2}$;但这结果是错误的,为什么呢?

最后一个格子的考虑不完备,E不与A和D相同,潜意识里认为AD也不同。但还存在A与D相同的情况。AD相同会有两种。所以AD不同时,D有两种,E只有一种选择,所以3*2*2*2*1,当AD相同时,D没有选择,E有两种选择,3*2*2*1*2就将公式抽象成3*2^{n-3}*3。这结果仍然是错误的,为什么呢?

因为D本质上只有两个选择,而上述方案给了D三个选择,多出了一个,为什么呢,因为D必须要与C不同,所以前面的系数不能是3*2*2了。(这里太烧脑了)

DP方法:
DP的求解过程本质就是如何去掉n。

回归到这个问题,就是如何去掉E;这里可以分成AD同色和不同色的情况,AD不同色,E只有一种选择,此时去掉E,前n-1个格子的颜色完全满足条件,所以就等同于求解f(n-1);如果AD同色,E有两种选择,但仅仅去掉E是不行的,因为AD同色不满足条件。那么此时去掉D,效果怎么样呢,因为CD不同色,所以此时AC不同色,前n-2个格子完全满足条件,所以满足f(n-2)的条件。所以最终的公式:f(n) = f(n-1) + 2*f(n-2)。

最后求解两个初始值,f(1) = 3 , f(2) = 6, f(3) = 6.这里求解一般容易漏掉3这个特殊值。因为这里的格子只有三类,起始位置,中间位置,结束位置。
这里的公式就很简单了。

也见到有其他的解法
// // 你必须定义一个 `main()` 函数入口。
#include <iostream>
#include <vector>
using namespace std;

void backtrace(vector<int>& path, int n, int& result){
    
    if(path.size()==n){
        result++;
//         for(int x: path){
//             cout << x << "  ";
//         }
//         cout << endl;
        return;
    }
    
    for(int i=0; i<3; i++){
        if(i==path[path.size()-1] || (path.size()==n-1 && i==path[0]))
            continue;
        path.push_back(i);
        backtrace(path, n, result);
        path.pop_back();
    }
}

int Method(int n){
    
    vector<int> path;
    int result=0;
    //这里i代表颜色
    for(int i=0; i<3; i++){
        path.push_back(i);
        backtrace(path, n, result);
        path.pop_back();
    }
    return result;
}


int main()
{
    cout << Method(2) << endl;
    cout << Method(10) << endl;
    return 0;
}

==============================================
def func(n):
  if n < 0 :
    return 0
  dp = [0] * (n+1)
  dp[1] = 3
  dp[2] = 6
  dp[3] = 6  
  if n <= 3: return dp[n]
  for i in range(4,n + 1):
    dp[i] = 2 * dp[i-2] + dp[i-1]
  return dp[-1]
print(func(4))

如何才能快速定位到使用DP规划求解问题呢,提高效率? 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值