动态规划入门学习

本文介绍了动态规划的两种常见方法——记忆化搜索和递推,通过实例展示了如何解决跳台阶问题、打家劫舍和背包问题,强调了优化时间和空间的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文内容主要来自于下面的链接,推荐大家观看学习动态规划(dp)入门 | 这tm才是入门动态规划的正确方式! | dfs记忆化搜索 | 全体起立!!_哔哩哔哩_bilibilid

引入:跳台阶问题

这个问题应该都很熟悉,大一的时候一般会当作例题来学函数递归

常规解法会得到一个时间复杂度O(2^n)的算法,分析发现有一半的递归是不必要的

暴力搜索

#include <iostream>

using namespace std;

int n;
int dfs(int x){
    if(x==1) return 1;
    else if(x==2) return 2;
    else{
        return dfs(x-1)+dfs(x-2);
    }

}

int main(){

    scanf("%d",&n)
    printf("%d",dfs(n));

    return 0;
}

记忆化搜索

通过记忆每一级台阶的方案数,达到递归树剪枝的效果

记忆化搜索 = dfs + 记录答案

#include <iostream>
using namespace std;
int mem[20];//记忆数组,空间换时间
int n;
int dfs(int x){
    if(mem[x]) return mem[x];//有记忆直接返回

    int sum = 0;
    if(x==1) sum = 1;
    else if(x==2) sum = 2;
    else{
        sum = dfs(x-1)+dfs(x-2);
    }
    mem[x] = sum;//记忆当前台阶方案总数
    return mem[x];
}

int main(){
    scanf("%d",&n);
    printf("%d",dfs(n));
    return 0;
}

递推(dp)

常用的递归从树根“递”到树的叶子,再由叶子“归”到根,但若是我们已有叶子节点的结果,是不是能只执行“归”的过程,从而达到递推到所需答案的效果(有点像数列的递推方程),使用迭代的方法

递推的公式 = dfs向下递归的公式

递推公式的初始值 = 递归的边界(出口)

#include <iostream>
using namespace std;

int f[20];

int main(){
    scanf("%d",&n);

    f[1] = 1;f[2] = 2;
    for (int i = 3; i <= n; i++)
    {
        f[i] = f[i-1] + f[i-2];
    }
    printf("%d",f[n]);
    // printf("%d",dfs(n));
    return 0;
}

还可以进一步优化空间,不用数组,用三个变量也可以得到答案,这里不在赘述

第一题:打家劫舍

暴力搜索

不是完全按力扣的输入输出和模板写的

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;

const int N = 100000;
int n;
int store[N];


int dfs(int x){
    if(x>n) return 0;
    else return max(dfs(x+1),dfs(x+2)+store[x]);
}
int main(){
    scanf("%d",&n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d",&store[i]);
    }
   cout<<dfs(1); 
    
    return 0;

}

记忆化搜索

按记忆化搜索的模板写,但是要注意记忆化搜索要注意实现记忆化搜索时要尽可能减少dfs的参数,方便实现记忆化搜索,因为记忆数组的下标数对应的是dfs的参数数,数组就是记录了函数的返回值

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100000;
int n;
int store[N];
int mem[N];

int dfs(int x){

    if(mem[x]) return mem[x];
    int sum = 0;
    if(x>n) sum = 0;
    else sum = max(dfs(x+1),dfs(x+2)+store[x]);

    mem[x] = sum;
    return sum;
}

int main(){
    scanf("%d",&n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d",&store[i]);
    }

   cout<<dfs(1); 
    
    return 0;

}



递推

递推公式就是上面dfs返回的式子

由此来写递推的代码

#include<iostream>
using namespace std;
const int N = 150;
int n;
int store[N];
int f[N];

int main(){
    scanf("%d",&n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d",&store[i]);
    }
    for (int i = n; i >= 1; i--)
    {
        f[i] = max(f[i+1],f[i+2]+store[i]);
    }
    cout<<f[1]; 
    
    return 0;

}

leecode正解

上面哪些步骤都做下来之后,其实就很容易做出这道题了

完全的代码

#include<iostream>
#include<vector>
using namespace std;

class Solution {
public:
    int rob(vector<int>& nums) {
        int f[152] = {0};
        for (int i = nums.size()-1; i >= 0; i--)
        {
            f[i] = max(f[i+1],f[i+2]+nums[i]);
        }
        return f[0];
    }
};

int main(){
    vector<int> nums;
    int item;
    while (scanf("%d",&item)!=EOF)
    {
        nums.push_back(item);
    }
    Solution S;
    cout<<S.rob(nums);
    

    return 0;

}



第二题:数字金字塔

重启了一下电脑忘记保存了,导致这题写的东西丢失了,直接看代码吧

记忆化搜索+DP

对DP进行了一次内存优化,由二维降到了一维

#include <iostream>
#include <math.h>
using namespace std;
int n = 0;
int temp = 0;
int mem[1001][1001];
int arr[1002][1002];
int f[1001];


int dfs(int i,int j){

    if(mem[i][j]) return mem[i][j];

    int sum = 0;
    if(i > n) 
        sum = 0;
    else 
        sum = max(dfs(i+1,j)+arr[i][j],dfs(i+1,j+1)+arr[i][j]);

    mem[i][j] = sum;
    return mem[i][j];

}

int main(){

    scanf("%d",&n);
    
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= i; j++)
        {
            scanf("%d",&arr[i][j]);
        }
        
    }
    for (int i = n; i >= 1; i--)
    {
        for (int j = 1; j <= i; j++)
        {
            f[j] = max(f[j]+arr[i][j],f[j+1]+arr[i][j]);
        }
        
    }
    
    cout<<f[1];
    cout<<dfs(1,1)

    return 0;
}

第三题:背包问题

记忆化搜索

#include <iostream>
#include<math.h>

using namespace std;

#define MAX 1010
int arr[MAX][2];
int n,b;
int mem[MAX][MAX];
int f[MAX][MAX];

int mymax(int a,int b,int c){
    if(a>=b&&a>=c){
        return a;
    }
    else if(b>=a&&b>=c){
        return b;
    }
    else{
        return c;
    }
}

int dfs(int i,int b){
    if(mem[i][b]) return mem[i][b];
    int sum = 0;

    if(i>n) sum = 0;
    else if(arr[i][0]>b){
        sum = dfs(i+1,b);
    }
    else if(arr[i][0]<=b){

        sum = mymax(dfs(i+1,b-arr[i][0])+arr[i][1] ,dfs(i,b-arr[i][0])+arr[i][1] , dfs(i+1,b) );
    }
    mem[i][b] = sum;
    return mem[i][b];
    
}

int main(){

    scanf("%d %d",&n,&b);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j < 2; j++)
        {
            scanf("%d",&arr[i][j]);
        }

    }
    
    cout<<dfs(1,b);

    return 0;
}

记忆化搜索已经AC

---------------------------------更新中-----------------------------------------

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值