从dfs到记忆化搜索再到动态规划

dfs是暴力解算,时间复杂度和空间复杂度都很废,我们可以从dfs到记忆化搜索再到动态规划优化时间复杂度,从动态规划到动态数组优化空间复杂度。

一、dp[n]

我们从一道经典的动态规划题--打家劫舍说起。

1.画出所有情况

假设这几家的现金是【50, 30, 12, 5】我们从第一家开始,可以把所有的情况画出来(打勾的表示抢劫,否则不抢)

每一家都可以选择抢劫或不抢劫。如果选了第一家,那就只能从第三家开始选;如果没有选,则可以从第二家开始选。这是每一轮递归要干的事情。

2.搞出递归关系

所以就有了这个由人话写成的函数:

//n    表示当前是第几家

//total       表示一共有几家人

int 从第n家开始拿到的钱的最大值(int n, int total)

{

        //结束条件

        如果n >= total,抢劫完毕,没法再抢劫了,返回0

        //关系式  

        从第n家开始拿到的钱的最大值 = max( 

                抢这家的钱!+ 从第n+2家开始拿到的钱的最大值 ,

                不抢这家的钱!+ 从第n+1家开始拿到的钱的最大值

        )

      返回 从第n家开始拿到的钱的最大值; 

}

转换成gcc认识的,就是:

//每一家的现金数量
//left    表示当前是第几家

//n       表示一共有几家人
int cashes[N] = {0};

int dfs(int left, int n)
{
    int temp;
    int rst = 0;
    if (left >= n)
    {
        return 0;
    }
    if (mono[left] != 0)
    {
        return mono[left];
    }
    rst = cashes[left] + dfs(left + 2, n);
    temp = cashes[left+1] + dfs(left + 3, n);
    if (temp > rst)
    {
        rst = temp;
    }
    mono[left] = rst;
    return rst;
}

3.加上记忆化搜索

在树状图中,我们很容易发现,节点12被计算了两次,如果我们能够把计算结果存其来就能极大的减少计算量。注意到函数的返回值只与参数left有关,我们就可以根据参数left来存储返回值。由于left是连续的,我们直接用数组存就好了。函数的逻辑就成了这样:

//n    表示当前是第几家

//total       表示一共有几家人

int 缓存[total] = {0};

int 从第n家开始拿到的钱的最大值(int n, int total)

{

        //结束条件

        如果缓存[n]  != 0,说明已经计算过,返回缓存[n] 

        如果n >= total,抢劫完毕,没法再抢劫了,返回0

        //关系式  

        从第n家开始拿到的钱的最大值 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值