目录
1、动态规划
最重要的:要推导出动态规划的转移方程,后续程序编写工作
设计思路:
1. 分析优化解的结构
2. 递归地定义最优解的代价
3. 自底向上地计算优化解的代价保存之,并获取构造最优解的信息
4. 根据构造最优解的信息构造优化解
53.最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int x[100004] , max ;
x[0] = nums[0] ;
max = x[0];
for(int i = 1 ; i < nums.size() ; i ++ ){
if( x[i-1] + nums[i] > nums[i] )
x[i] = x[i-1] + nums[i]; //转移方程
else
x[i] = nums[i];
if(max < x[i])
max = x[i];
}
return max ;
}
};
746.使用最小花费爬楼梯
输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int x[1005];
x[0] = cost[0];
x[1] = cost[1];
for(int i = 2 ; i < cost.size() ; i ++ )
x[i] = min( x[i-1] + cost[i] , x[i-2] + cost[i] );
return min(x[cost.size() - 1] , x[cost.size() - 2]);
}
};
119.杨辉三角II
给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
输入: rowIndex = 1
输出: [1,1]
输入: rowIndex = 3
输出: [1,3,3,1]
class Solution {
public:
vector<int> getRow(int rowIndex) {
vector<int> dp(rowIndex+1 , 1);
int a = dp[0], b = dp[1];
for(int j = 2 ; j <= rowIndex ; j ++){
a = dp[0];b = dp[1];
for( int i = 1 ; i < j ; i ++ )
{
dp[i] = a + b;
a = b ;
b = dp[i+1];
}
}
return dp;
}
};
338.比特位计数
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
输入: n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
class Solution {
public:
vector<int> countBits(int n) {
vector<int> dp(n+1 , 1);
int x = 4 ;
dp[0] = 0;
if(n>3) //有可能是n = 1,2,所以不加if会越界
dp[3] = 2;
for(int i = 5 ; i <= n ; i ++){
if(i == x*2)
x = x*2;
else
dp[i] += dp[i - x];
}
return dp;
}
};
42.接雨水(困难)
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
class Solution {
public:
int trap(vector<int>& height) {
int result = 0 ;
int n = height.size();
//分别找到每一个位置往左和往右最高的,遍历2次保存到left,right
//最后遍历一遍,取左右最大值中的最小的一个,减去本身高度即为水量
int left[20005],rigth[20005];
left[0] = height[0];
rigth[n-1] = height[n-1];
for(int i = 1 ; i < n ; i ++){
left[i] = max(left[i-1] , height[i]); //保存自己和左边之间最大的
}
for(int i = n-2 ; i >=0 ; i -- ){
rigth[i] = max( rigth[i+1] , height[i]);//保存自己和右边之间最大的
}
for(int i = 1 ; i < n-1 ; i ++ ){
result += (min(left[i] , rigth[i]) - height[i] );
}
// 318/320 最后2个错在了超时上,yue
// 按层搜索,从高度为1的开始,每一层的水相加
// height.push_back(0); //在所有数据最后加入一个0,否则后面会出现越界
// if(n == 1)
// return 0;
// for(int i = 1 ; i < n ; i ++) // 找到最大的一个
// if( height[i] > max )
// max = height[i];
// for(int i = 1 ; i <= max; i ++ ){
// int b = 0 ;
// int x1 = 0 ;
// for(int j = 0 ; j < n ; j ++ ){
// if(height[j] >= i && b == 0 && height[j+1] < i){ //前面一个
// b = 1 ;
// x1 = j ;
// }
// else if(height[j] >= i && b == 1){ // 后面一个
// b = 0 ;
// j--; //让后一个称为前面一个
// result =result + (j - x1 );
// //cout <<result;
// }
// }
// }
return result;
}
};
198.打家劫舍 (中等)
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
int dp[105];
if(n == 1)
return nums[0];
dp[0] = nums[0];
dp[1] = max(nums[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];
}
};
62.不同路径 (中等)
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右
- 向下 -> 向右 -> 向下
class Solution {
public:
int uniquePaths(int m, int n) {
int dp[105][105];
dp[1][1] = 1;
for(int i = 1; i <= m ; i ++ )
dp[i][1] = 1;
for(int j = 1 ; j <= n ; j ++)
dp[1][j] = 1;
for(int i = 2 ; i <= m ; i ++ ){
for(int j = 2 ; j <= n ; j ++){
dp[i][j] = dp[i-1][j] + dp[i][j-1]; //当前点的数量= 左边的点+上面点的
}
}
return dp[m][n];
}
};
63.不同路径II (中等)
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
- 向右 -> 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右 -> 向右
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int dp[105][105];
int m = obstacleGrid.size();
int n = obstacleGrid[0].size();
for(int i = 1 ; i <= m ; i ++ ){
for(int j = 1 ; j <= n ; j ++){
//将原来的取反,0代表不可达,1代表可达,为后续好计算
if(obstacleGrid[i-1][j-1] == 0)
dp[i][j] = 1 ;
else
dp[i][j] = 0 ;
}
}
for(int i = 1 ; i <= m ; i ++ ){
for(int j = 1 ; j <= n ; j ++){
if(i == 1 && j != 1 && dp[i][j] != 0) // 第一列
dp[i][j] = dp[i][j-1];
if(j == 1 && i != 1 && dp[i][j] != 0) //第一行
dp[i][j] = dp[i-1][j];
if(i!= 1 && j != 1 && dp[i][j] != 0) // 其他元素
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m][n];
}
};
64.最小路径和(中等)
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
首先将第一行和第一列进行处理
然后根据状态转移方程计算即可
grid[i][j] += min(grid[i-1][j] , grid[i][j-1]);
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int dp[205][205] ;
int m = grid.size();
int n = grid[0].size();
for(int i = 1; i < m ; i ++ )
grid[i][0] += grid[i-1][0];
for(int j = 1 ; j < n ; j ++ )
grid[0][j] += grid[0][j-1];
for(int i = 1 ; i < m ; i ++ ){
for(int j = 1 ; j < n ; j ++ ){
grid[i][j] += min(grid[i-1][j] , grid[i][j-1]);
//当前位置+ min(左,上)
}
}
return grid[m-1][n-1];
}
};
120.三角形最小路径和(中等)
给定一个三角形 triangle ,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
白想了半天用一维dp保存,弄个半个多小时,总是有问题,总是会多加一个元素
后来一想,直接放原来triangle里多方便。
空间直接从o(n)变为o(1)
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int n = triangle.size();
//int dp[205];
//memset(dp , 0 , sizeof(dp));
//dp[0] = triangle[0][0];
for(int i = 1 ; i < n ; i ++){
for(int j = 0 ; j <= i ; j ++ ){
if( j == 0 )
triangle[i][j] += triangle[i-1][j];
else if( j == i )
triangle[i][j] += triangle[i-1][j-1];
else
triangle[i][j] += min(triangle[i-1][j] , triangle[i-1][j-1]);
}
}
int result = triangle[n-1][0] ;
for(int i = 0 ; i < n ; i ++ )
if(result > triangle[n-1][i])
result = triangle[n-1][i];
return result;
}
};
931.下降路径最小和
给你一个 n x n 的 方形 整数数组 matrix ,请你找出并返回通过 matrix 的下降路径 的 最小和 。
下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置 (row, col) 的下一个元素应当是 (row + 1, col - 1)、(row + 1, col) 或者 (row + 1, col + 1) 。
输入:matrix = [[2,1,3],[6,5,4],[7,8,9]]
输出:13
解释:如图所示,为和最小的两条下降路径
主要分类好,分成第一列,最后列,还有其他列
然后找到状态转移方程就行
class Solution {
public:
int minFallingPathSum(vector<vector<int>>& matrix) {
int n = matrix.size();
int result ;
for(int i = 1 ; i < n ; i ++ ){
for(int j = 0 ; j < n ; j ++){
if(j == 0)//最左列 ,只能加上方,右上方
matrix[i][j] += min(matrix[i-1][j] , matrix[i-1][j+1]);
else if (j == n-1) // 最右列, 可以加 上,左上
matrix[i][j] += min(matrix[i-1][j] , matrix[i-1][j-1]);
else // 中间列 , 可以加左上,上,右上
matrix[i][j] += min(min(matrix[i-1][j-1] , matrix[i-1][j]) , matrix[i-1][j+1]);
}
}
result = matrix[n-1][0] ;
for(int j = 0 ; j < n ; j ++)
if(matrix[n-1][j] < result)
result = matrix[n-1][j];
return result;
}
};
1289.下降路径最小和 II
给你一个 n x n 整数矩阵 arr ,请你返回 非零偏移下降路径 数字和的最小值。
非零偏移下降路径 定义为:从 arr 数组中的每一行选择一个数字,且按顺序选出来的数字中,相邻数字不在原数组的同一列。
输入:arr = [[1,2,3],[4,5,6],[7,8,9]]
输出:13
解释:
所有非零偏移下降路径包括:
[1,5,9], [1,5,7], [1,6,7], [1,6,8],
[2,4,8], [2,4,9], [2,6,7], [2,6,8],
[3,4,8], [3,4,9], [3,5,7], [3,5,9]
下降路径中数字和最小的是 [1,5,7] ,所以答案是 13 。
由于不能同列,所以除了找到每行最小的,还需要找到第二小的
除了最小的那列,其他列都取这个值
当到最小列时,则找第二小值
15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]