动态规划一般过程为:
题目:121. 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
1.暴力求解:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int max=0,n=prices.size();
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(prices[j]-prices[i]>max)
max=prices[j]-prices[i];
}
}
return max;
}
};
- 动态规划
确定状态找到状态转移方程:
如果在第i天出售股票,那么能获得的最大利润就是当前的价格减去前i-1天的最低值,所以我们可以记录购入时的最小值,得到其状态转移方程为:dp[i] = min(d[i-1],prices[i])。
初始化:
int dp[0]=prices[0], max = 0;
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n<=1)
return 0;
int dp[n],max=0;
dp[0]=prices[0];
for(int i=1;i<n;i++)
{
dp[i]=(dp[i-1]<prices[i])?dp[i-1]:prices[i];
max=(prices[i]-dp[i])>max?prices[i]-dp[i]:max;
}
return max;
}
};
这里可以对内存优化,因为我们一直使用的是dp[i],所以可以用一个变量保存,就不用数组了。
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n<=1)
return 0;
int min,max=0;
min=prices[0];
for(int i=1;i<n;i++)
{
min=(min<prices[i])?min:prices[i];
max=(prices[i]-min)>max?prices[i]-min:max;
}
return max;
}
题目:70. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
1.确定状态
在爬到某一层台阶时前一步有两种方式,一种是走了一阶到达该台阶,一种是走了两阶到达该台阶。假设dp[i]表示到达第i层有多少种方式,那么从后往前看每次dp[i]都依赖于前面的状态。
2.转移方程
可以看出,首先是dp[i - 1],上i-1层楼梯,有dp[i - 1]种方法,那么再一步跳一个台阶不就是dp[i]了么。
还有就是dp[i - 2],上i-2层楼梯,有dp[i - 2]种方法,那么再一步跳两个台阶不就是dp[i]了么。那么dp[i]就是 dp[i - 1]与dp[i - 2]之和!转移方程即为dp[i] = dp[i - 1] + dp[i - 2] 。
3.初始条件和边界
dp[1]是到达第一个台阶只有1中方式,dp[2]是到达第二个台阶,可以走两次一步或者一次两步共2种方式。
int climbStairs(int n) {
if (n <= 1) return n; // 因为下面直接对dp[2]操作了,防止空指针
int dp[n+1];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) { // 注意i是从3开始的
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
题目:746. 使用最小花费爬楼梯
数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。
每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。
请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int s=cost.size();
int dp[s+1];
dp[0]=cost[0],dp[1]=cost[1];
for(int i=2;i<s;i++){
dp[i]=dp[i-1]<dp[i-2]?cost[i]+dp[i-1]:cost[i]+dp[i-2];
}
dp[s]=dp[s-1]<dp[s-2]?dp[s-1]: dp[s-2];
return dp[s];
}
};
题目:1646. 获取生成数组中的最大值
给你一个整数 n 。按下述规则生成一个长度为 n + 1 的数组 nums :
nums[0] = 0
nums[1] = 1
当 2 <= 2 * i <= n 时,nums[2 * i] = nums[i]
当 2 <= 2 * i + 1 <= n 时,nums[2 * i + 1] = nums[i] + nums[i + 1]
返回生成数组 nums 中的 最大 值。
这个题分奇数偶数计算,也可以把奇数偶数合并起来计算,利用偶数mod2为0和向下取整计算、
int getMaximumGenerated(int n) {
int dp[n+1];
if(n==0)
return 0;
if(n==1)
return 1;
dp[0]=0,dp[1]=1;
int max=1;
for(int i=2;i<=n;i++){
if(i%2==0)
dp[i]=dp[i/2];
else
dp[i]=dp[(i-1)/2]+dp[(i+1)/2];
if(dp[i]>max)
max=dp[i];
}
return max;
}
合并后:
for(int i=2;i<=n;i++){
dp[i] = dp[i / 2] + i % 2 * dp[i / 2 + 1];
if(dp[i]>max)
max=dp[i];
}
题目:LCS 01. 下载插件
小扣打算给自己的 VS code 安装使用插件,初始状态下带宽每分钟可以完成 1 个插件的下载。假定每分钟选择以下两种策略之一:
使用当前带宽下载插件
将带宽加倍(下载插件数量随之加倍)
请返回小扣完成下载 n 个插件最少需要多少分钟。
注意:实际的下载的插件数量可以超过 n 个
这个题最优解法是贪心,但也可以参考贪心的思想用dp做。这里参考了题解的dp思路。
设dp[i]表示下载第i个插件所需的最少时间,那么dp[i]可以从两种方式转变为来:
- 下载i-1个文件所需的最短时间 +1s,
- 能够下载i/2个文件所需的时间提升带宽 +1s
(为了避免i为奇数时除以2由于向下取整的问题,所以先i+1)
所以有dp[i] = min(dp[(i+1)/2],dp[i-1])+1
int leastMinutes(int n) {
int dp[n+1];
dp[0] = 0;dp[1] = 1;
for(int i = 2;i<=n;i++){
dp[i] = min(dp[(i+1)/2],dp[i-1])+1;
}
return dp[n];
}
题目:LCP 07. 传递信息
小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下:
有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0
每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传信息的关系是单向的(比如 A 可以向 B 传信息,但 B 不能向 A 传信息)。
每轮信息必须需要传递给另一个人,且信息可重复经过同一个人
给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。
这道题没想出来,贴下官方解法:
class Solution {
public:
int numWays(int n, vector<vector<int>>& relation, int k) {
vector<vector<int>> dp(k + 1, vector<int>(n));
dp[0][0] = 1;
for (int i = 0; i < k; i++) {
for (auto& edge : relation) {
int src = edge[0], dst = edge[1];
dp[i + 1][dst] += dp[i][src];
}
}
return dp[k][n - 1];
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/chuan-di-xin-xi/solution/chuan-di-xin-xi-by-leetcode-solution/