动态规划的简单套路(C++描述)
创始人
2024-02-17 20:48:01
0

动态规划

作者:Cukor丘克

文章目录

  • 动态规划
    • 引导
    • 计数
    • 最值
    • 存在性

通过例子学习动态规划。动态规划是解决递归重复计算的方法。

能解决的问题:

  1. 计数
  2. 最值
  3. 存在性

解题步骤:

  1. 最后一步
  2. dp数组的含义
  3. 状态转移方程
  4. dp数组的初始化
  5. 实例代入

使用的编程语言的是C++.

引导

斐波那契数列

什么是斐波那契数列?

前两个数字是1,从第3个数开始,是它的前两个数之和。

代码实现:

递归方式:

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

动态规划:

// 最后一步:前面的数据已经算好,最后一个数据只需要拿到它的前两个数据即可。
// dp数组的含义:第i个数的斐波那契数是dp[i]
// 状态转移方程:dp[i] = dp[i-1] + dp[i-2]
// dp数组的初始化:dp[0] = 0; dp[1] = 1;
// 实例代入:dp[5] = dp[4] + dp[3];
//            dp[4] = dp[3] + dp[2];    dp[3] = dp[2] + dp[1];        
//            dp[2] = dp[1] + dp[0];    
// 过程:dp[5] = 5;
//            dp[4] = 3    dp[3] = 2;        
//            dp[2] = 1;
int fib(int n) {if (n <= 1) return n;vector dp(n+1);dp[0] = 0; dp[1] = 1;for (int i = 2; i <= n; i++) {dp[i] = dp[i-1] + dp[i-2];}return dp[n];
}

计数

  • 爬楼梯
  • 机器人走路

爬楼梯:你爬楼梯,你可以一次走一步,也一次可以走两个。现有n阶楼梯,你有多少种方法爬到顶部。

/*
最后一步:要么第n-1阶,要么n-2阶,只需要知道走到n-1阶有多少种方法,和走到n-2阶有多少种方法。dp数组的含义:走到n阶楼梯的方法数是dp[n]状态转移方程:dp[n] = dp[n-1] + dp[n-2]初始化:dp[1] = 1; dp[2] = 2; 
*/
int climb_stairs(int n) {if (n <= 1)return n;vector dp(n + 1);dp[1] = 1;dp[2] = dp[1] + 1;for (int i = 3; i <= n; i++) {dp[i] = dp[i - 1] + dp[i - 2];}return dp[n];
}

机器人走路:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

/*
最后一步:最后一步的前一个状态,要么在(m-1,n)的位置,要么在(m,n-1)的位置,只需要再向下或向右走一步即可到达(m,n)所以走到(m,n)位置的方法数位走到(m-1,n)的方法数和走到(m,n-1)的方法数之和。
dp数组的含义:(0,0)位置走到(i,j)位置的方法数是dp[i][j]
状态转移方程:dp[i][j] = dp[i-1][j] + dp[i][j-1];
初始化:第一行和第一列都是1 dp[i][0] = 1; dp[0][j] = 1;
*/
int uniquePaths(int m, int n) {vector> dp(m,vector(n));for(int i = 0; i < m; i++) {dp[i][0] = 1;}for(int j = 0; j < n; j++) {dp[0][j] = 1;}for (int i = 1; i < m; i++) {for (int j = 1; j < n; j++) {dp[i][j] = dp[i-1][j] + dp[i][j-1];}}return dp[m-1][n-1];
}
//-----------------------------------------------int Solution::uniquePaths(int m, int n) {std::vector> dp(m, std::vector(n));for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (i == 0 || j == 0) {dp[i][j] = 1;} else {dp[i][j] = dp[i - 1][j] + dp[i][j - 1];}}}for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {std::cout << dp[i][j] << " ";}std::cout << "\n";}std::cout << std::endl;return dp[m - 1][n - 1];
}

最值

  • 打家劫舍
  • 买东西(硬币支付问题)

打家劫舍:你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

题目的别名:求不相邻元素之和最大。

/*
最后一步:前i-1个数已经确定最大和,只需要判断第i个数是否纳入最大和中。纳入:最大和则更新为前i-2时的状态加当前数组中的值。不纳入:第i-1个状态
dp数组的含义:前i个数确定不相邻元素之和最大和为dp[i]
状态转移方程:dp[i] = max(dp[i-2] + nums[i],dp[i-1]);
初始化:dp[0] = nums[0]; dp[1] = max(dp[0], nums[1]);
*/
int rob(vector &nums){int n = nums.size();vector dp(n);dp[0] = nums[0]; dp[1] = max(dp[0], nums[1]);for (int i = 2; i < n; i++) {dp[i] = max(dp[i-2] + nums[i],dp[i-1]);}return dp[n-1];
}

买东西(硬币支付问题):给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1

你可以认为每种硬币的数量是无限的。

/*
最后一步:前面已经确定使用硬币数最少的方案。只需要确定最后一枚硬币到底是哪种面额的。如果剩余的钱不能用已有的面额硬币支付,则无法确            定最少硬币数。
dp数组的含义:i元的商品使用的硬币数最少是dp[i]
状态转移方程:dp[i] = min(dp[i - coins[j]]+1, dp[i])
初始化:dp[0] = 0dp[i] = INT_MAX,   i = 1,2,...,n
*/
int coinChange(vector& coins, int n) {vector dp(n+1, INT_MAX);dp[0] = 0;int m = coins.size();for (int i = 1; i <= n; i++) {for (int j = 0; j < m; j++){if (i >= coins[j] && dp[i - coins[j]] != INT_MAX){dp[i] = min(dp[i - coins[j]] + 1, dp[i]);} }}return dp[n] == INT_MAX ? -1 : dp[n];
}

存在性

  • 拼数字

拼数字:在一个数组里面找到一些元素相加起来是否和num的值相等,如果能拼成num则返回true,否则返回false.

例子:

num=11

arr ={4,5,2,7}

返回true

num=3

arr ={4,5,2,7}

返回false

/*
最后一步:如果最后一步的前一个状态能拼成num,则最后一个状态也能拼成num,如果最后一步的前一个状态不能拼成num,就看加上数组的最后一个数能否拼成num,如果能就返回true,否则返回false.
dp数组的含义:i+1个数的数组能否拼成j,能则dp[i][j]=true,不能则dp[i][j]=false
状态转移方程:if(dp[i-1][j] == true) dp[i][j] = trueelse dp[i][j] = dp[i-1][j-arr[i]];
初始化:dp[0][j] = false; j = 0,1,...,numdp[0][arr[0]] = true;
*/bool spell_numbers(vector& arr, int num){int n = arr.size();vector> dp(n,vector(num+1));for(int j = 0; j <= num; j++) {dp[0][j] = false;}if (num >= arr[0])dp[0][arr[0]] = true;for (int i = 1; i < n; i++) {for (int j = 0; j <= num; j++) {dp[i][j] = dp[i-1][j];if (j >= arr[i])dp[i][j] = j == arr[i] || dp[i-1][j] || dp[i-1][j-arr[i]];}}return dp[n-1][num];
}

动态规划的简单入门套路就先到这了,以下是我在b站的视频教程。

动态规划的简单套路(C++描述)


谢谢大家的支持啦!!!

相关内容

热门资讯

CBA潜力赛为何打成“老将赛”... 计时钟归零,双方教练握手致意,观众开始退场,CBA联赛的正赛宣告结束。然而球场并未就此沉寂,替补席上...
“手术钻头断裂遗留患者体内”,... 12月21日,湖南祁阳市卫生健康局发布情况通报称,近日,有媒体报道祁阳市中医医院发生骨科手术钻头断裂...
代驾纠纷 代驾时撞伤行人、车辆发生故障…… 这些都和车主无关,应由代驾赔偿? 观点: 使用代驾服务并非将所有...
公司股东与妻子分居期间出轨女下... 近日据报道,宁夏永宁县人民法院一审查明公司股东李某乙在与妻子李某甲分居期间,与公司女员工马某某存在不...
动物学家、律师和创作者,Thi... 12月21日,以“一起·了不起”为主题的2025 ThinkPad黑FUN礼在京举办。活动现场,律师...
徐奇渊:扩内需与对外政策紧密相... 近日,中国海关总署发布了一组数据令人关注:2025年前11个月,我国货物贸易顺差达到1.08万亿美元...
46岁上海独居女子不幸离世,官... 居住在上海虹口区46岁的蒋女士因突发脑溢血于今年10月入院,远亲吴先生与其公司共同垫付了医药费,但她...
威海市汽车以旧换新补贴政策调整... 根据稳妥有序开展消费品以旧换新工作统一部署,经研究决定,对我市汽车以旧换新补贴政策进行调整。现将有关...
动物学家、律师、创作者都pic... 12月21日,在2025 ThinkPad黑FUN礼现场,三名专业领域用户用真实案例诠释了Think...