动态规划理论
动态规划搞清楚dp数组的含义,j代表什么,dp[j]代表什么。
1、爬楼梯
(1)题目描述以及输入输出
(1)题目描述:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
(2)输入输出描述:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
关键思路:
第三阶可以是第二阶跨1步上去,也可以是第1阶跨两步上去。
i代表阶数,dp[i]代表需要的步数
(2)代码块
class Solution {
public:
int climbStairs(int n)
{
vector<int> dp(n+1,0);
dp[1] = 1;
dp[2] = 2;
for(int i = 3;i<=n;++i)
{
dp[i] = dp[i-1]+dp[i-2];
}
return dp[n];
}
};
2、杨辉三角
(1)题目描述以及输入输出
(1)题目描述:
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
(2)输入输出描述:
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
关键思路:
初始化每行为1
tmp[j] = record[i-1][j-1] + record[i-1][j];计算每行数据
(2)代码块
class Solution {
public:
vector<vector<int>> generate(int numRows)
{
vector<vector<int>> record;
for(int i = 0;i<numRows;++i)
{
vector<int> tmp(i+1,1);
for(int j = 1;j<i;j++)
{
tmp[j] = record[i-1][j-1] + record[i-1][j];
}
record.push_back(tmp);
}
return record;
}
};
3、打家劫舍
(1)题目描述以及输入输出
(1)题目描述:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
(2)输入输出描述:
输入:[1,2,3,1]
输出:4
关键思路:
i代表第几家,dp[i]代表盗窃的最高金额。相邻两家会触发警报。因此dp[i] = max(dp[i-1],dp[i-2]+nums[i])
(2)代码块
class Solution {
public:
int rob(vector<int>& nums)
{
vector<int> dp(nums.size(),0);
if(nums.size()==1)
return nums[0];
dp[0] = nums[0];
dp[1] = max(nums[0],nums[1]);
for(int i = 2;i<nums.size();++i)
{
dp[i] = max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[nums.size()-1];
}
};
4、零钱兑换
(1)题目描述以及输入输出
(1)题目描述:
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
(2)输入输出描述:
输入:coins = [1, 2, 5], amount = 11
输出:3
关键思路:
i代表目标钱币,dp[i]代表凑齐该钱币需要的最少钱币数。本质还是暴力循环,遍历第二个钱币时,可以使用第一种钱币凑出
(2)代码块
class Solution {
public:
int coinChange(vector<int>& coins, int amount)
{
vector<int> dp(amount+1,INT_MAX);
dp[0] = 0;
for(int i = 0;i<coins.size();++i)
{
for(int j = coins[i];j<=amount;++j)
{
if(dp[j-coins[i]] != INT_MAX)
dp[j] = min(dp[j],dp[j-coins[i]]+1);
}
}
if(dp[amount] == INT_MAX)return -1;
return dp[amount];
}
};
5、完全平方数
(1)题目描述以及输入输出
(1)题目描述:
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量
(2)输入输出描述:
输入:n = 12
输出:3
关键思路:
平方数作为物品,目标数作为背包
(2)代码块
class Solution {
public:
int numSquares(int n)
{
vector<int> dp(n+1,INT_MAX);
dp[0] = 0;
for(int i = 1;i*i<=n;++i)
{
for(int j = i*i;j<=n;++j)
{
if(dp[j-i*i] != INT_MAX)
dp[j] = min(dp[j],dp[j-i*i]+1);
}
}
return dp[n];
}
};
6、最小路径和
(1)题目描述以及输入输出
(1)题目描述:
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
(2)输入输出描述:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
关键思路:
dp[i][j]代表走到这个格子需要的最小路径,先初始化上,左边界。
dp[i][j] += min(dp[i-1][j],dp[i][j-1])+grid[i][j]; // 上次最短路径+本次路径
(2)代码块
class Solution {
public:
int minPathSum(vector<vector<int>>& grid)
{
vector<vector<int>> dp(grid.size(),vector<int>(grid[0].size()));
dp[0][0] = grid[0][0];
for(int i = 1;i<grid.size();++i)
dp[i][0] = dp[i-1][0] +grid[i][0];
for(int i = 1;i<grid[0].size();++i)
dp[0][i] = dp[0][i-1] + grid[0][i];
for(int i = 1;i<grid.size();++i)
{
for(int j = 1;j<grid[0].size();++j)
dp[i][j] += min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
return dp[grid.size()-1][grid[0].size()-1];
}
};
7、不同路径
(1)题目描述以及输入输出
(1)题目描述:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
(2)输入输出描述:
输入:m = 3, n = 7
输出:28
关键思路:
record[i][j]代表走到i,j的不同走法
record[i][j] = record[i-1][j] + record[i][j-1];
(2)代码块
class Solution {
public:
int uniquePaths(int m, int n)
{
vector<vector<int>> record(m, vector<int>(n, 0));;
for(int i = 0;i<m;++i)
{
record[i][0] = 1;
}
for(int i = 0;i<n;++i)
{
record[0][i] = 1;
}
for(int i = 1;i<m;++i)
{
for(int j = 1;j<n;++j)
{
record[i][j] = record[i-1][j] + record[i][j-1];
}
}
return record[m-1][n-1];
}
};
8、单词拆分
(1)题目描述以及输入输出
(1)题目描述:
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。
(2)输入输出描述:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
关键思路:
遍历字符串,分割字符串,dp[i]代表前i个字符是否可拼接
(2)代码块
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict)
{
unordered_set<string> record(wordDict.begin(),wordDict.end());
vector<bool> dp(s.length()+1,false);
dp[0] = true;
for(int i = 1;i<=s.length();++i)
{
for(int j = 0;j<i;++j)
{
string str = s.substr(j,i-j);
if(dp[j] && record.count(str))
dp[i] = true;
}
}
return dp[s.length()];
}
};
9、最长递增子序列
(1)题目描述以及输入输出
(1)题目描述:
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的
子序列
(2)输入输出描述:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
关键思路:
i表示第几个序列,dp[i]表示当前序列的最长子序列的值
(2)代码块
class Solution {
public:
int lengthOfLIS(vector<int>& nums)
{
vector<int> dp(nums.size(),1);
int maxsize = 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],dp[j]+1);
maxsize = max(maxsize,dp[i]);
}
}
}
return maxsize;
}
};