本文记录自己的学习心得和一些要点,墙裂推荐参考此连接进行学习
一、从斐波那契入手
记忆搜索算法实现斐波那契
int helper(vector<int>& memo, int n){
if (n == 1 || n == 2)return 1;
if (memo[n] != 0) return memo[n];//只要不是0就会记录下来
memo[n] = helper(memo, n - 1) + helper(memo, n - 2);
return memo[n];
}
int fib1(int n){
if (n < 1) return 0;//n = 0的情况
vector<int> meno(n + 1, 0);
int res = helper(meno, n);
return res;
}
自底向上实现斐波那契数列
int fib3(int n){
vector<int>dp(n+1,0);
dp[1] = dp[2] = 1;
for (int i = 3; i <= n; ++i){
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
优化一下空间,之和两个状态相关,可以使用两个变量替代
int fib4(int n){
if (n == 1 || n == 2) return 1;
int sum = 0;
int pre = 1;//初始状态
int cur = 1;//初始状态
for (int i = 3; i <= n; ++i){
sum = pre + cur;
cur = sum;//状态转移
pre = cur;//状态转移
}
return cur;
}
二、凑零钱问题
主要找到状态转移方程和初始条件就可以了,状态转移方程一般时按照题目思路写几个,写着写着归纳法就出来了,初始条件也一样。写完之后就可以按照自底向上的方法法写出代码了。
有了状态转移方程和初始条件就可以写了.
int combinechange(vector<int>&coins,int n){
if (n < 0) return -1;
vector<int> dp(n + 1, 0);
dp[0] = 0;
for (int i = 1; i <= n; ++i){//计算所有值
int res = INT_MAX;
for (int j = 0; j < coins.size(); ++j){//遍历coins,计算每一个值都需要所有的coins
if (i>=coins[j]){//满足i>coins[j],否则无解
res = min(res, 1 + dp[i - coins[j]]);
}
else break;
}
dp[i] = res;
}
for (int i = 0; i < dp.size(); ++i){
cout << dp[i] << endl;
}
return dp[n];
}
修改一下dp数组初始条件得到更简洁的代码:
int combinechange1(vector<int>&coins, int n){
if (n < 0) return -1;
vector<int> dp(n + 1, n);//dp初始化为n,最好选择小的更新即可
dp[0] = 0;
for (int i = 1; i <= n; ++i){
for (int j = 0; j < coins.size(); ++j){
if (i >= coins[j]){
dp[i] = min(dp[i], 1 + dp[i - coins[j]]);
}
else break;
}
}
for (int i = 0; i < dp.size(); ++i){
cout << dp[i] << endl;
}
return dp[n];
}
这里的状态方程中前一个状态为dp[i-coins[j]],故不像斐波那契那样只和两个状态有关,这里的dp数组就无法像斐波那契那样直使用两个变量去更新了。
三、最长递增子序列问题(力扣300)
int lengthOfLIS(vector<int>& nums) {
if(nums.size()==0) return 0;
vector<int> dp(nums.size(),1);
dp[0]=1;
for(int i = 1;i<nums.size();++i){
for(int j = 0;j<i;++j){
if(nums[i]>nums[j]){
dp[i] = max(dp[i],1+dp[j]);
}
}
}
int res = 0;
for(int i = 0;i<dp.size();++i){
res = max(res,dp[i]);
}
return res;
}
四、跳跃到数组最后一个元素需要最少的步数(力扣45)
趁热打铁,这个题和上一题套路都是一样的
数组1:[2,3,1,1,4] 从2跳到3再跳到最后,需要两步
数组2:[3,2,1,0,4] 无法跳到组后一个元素,返回-1
int jump(vector<int>& nums) {
if(nums.size()==0) return 0;
vector<int>dp(nums.size(),INT_MAX);
dp[0]=0;
for(int i = 1;i<nums.size();++i){
for(int j = 0;j<i;++j){
if(nums[j]>=(i-j)){
dp[i] = min(dp[i],1+dp[j]);
}
}
}
return dp[dp.size()-1];
}
这个题考的应该是贪心,用动态规划做并不是最优的,对于全1也通不过。。。先不管了不影响我们理解动态规划。
五、最大子序和
继续趁热打铁做一下最大子序和,一样的套路
int maxSubArray(vector<int>& nums) {
vector<int> dp(nums.size(),INT_MIN);
dp[0] = nums[0];
for(int i = 1;i<nums.size();++i){
dp[i] = max(nums[i],nums[i]+dp[i-1]);
}
int res = dp[0];
for(int i=0;i<dp.size();++i){
res = max(res,dp[i]);
}
return res;
}