LeetCode 64. 最小路径和
打了一天游戏,没时间学习,要努力辽。
今天只是刷了一题leetcode,暂时以动态规划相关题型为复习点,编点工程代码。
新建./include/Solution.h
#include <vector>
using namespace std;
class Solution {
public:
int minPathSum(vector<vector<int>> &grid);
};
新建./source/Solution.cpp
#include <vector>
#include <algorithm>
#include "Solution.h"
using namespace std;
int Solution::minPathSum(vector<vector<int>> &grid) {
int rows = grid.size();
int cols = grid[0].size();
auto dp = vector<vector<int>> (rows, vector<int> (cols));
dp[0][0] = grid[0][0];
for(int i = 1; i < rows; i++) {
dp[i][0] = dp[i-1][0] + grid[i][0];
}
for(int i = 1; i < cols; i++) {
dp[0][i] = dp[0][i-1] + grid[0][i];
}
for(int i = 1; i < rows; i++) {
for(int j = 1; j < cols; j++) {
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
}
}
return dp[rows - 1][cols - 1];
}
新建./main.cpp
#include <vector>
#include <iostream>
#include "Solution.h"
using namespace std;
int main(int argc, char **argv) {
int n,m;
cin >> n >> m;
auto grid = vector<vector<int>> (n, vector<int> (m));
for (auto i = 0; i < n; i++) {
for (auto j = 0; j < m; j++) {
cin >> grid[i][j];
}
}
Solution mySolution;
cout << mySolution.minPathSum(grid) <<endl;
return 0;
}
新建./CMakeLists.txt
#声明要求的cmake最低版本
cmake_minimum_required(VERSION 3.0)
#声明一个cmake工程
project(Solution)
include_directories(include)
#添加一个可执行程序
#语法:add_executable(程序名 源代码文件)
add_executable(main_cmake main.cpp source/Solution.cpp)
执行
mkdir build
cd build
cmake ..
make
./main_cmake
测试用例
3
3
1 3 1 1 5 1 4 2 1
测试结果
7
LeetCode 72. 编辑距离
今天又是做帕鲁的一天,强撑着学点。
从字符串 S1 经过增/删/替换 转换到 S2 与
从字符串 S2 经过删/增/替换 转换到 S1 互为逆过程
上图中
1号单元记入字符串 S2 和字符串 S1 互转需要的最小步数
2号单元记入字符串 S2 和字符串( S1 + C1 )互转需要的最小步数
3号单元记入字符串( S2 + C2 )和字符串 S1 互转需要的最小步数
那么
4号单元可由1、2、3号单元内容计算得出。
class Solution {
public:
int minDistance(string word1, string word2) {
int s1 = word1.size();
int s2 = word2.size();
auto dp = vector<vector<int>>(s1 + 1, vector<int>(s2 + 1));
for (int i = 1; i <= s2; i++) {
dp[0][i] = i;
}
for (int i = 1; i <= s1; i++) {
dp[i][0] = i;
}
for (int i = 1; i <= s1; i++) {
for (int j = 1; j <= s2; j++) {
if (word1[i - 1] == word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
}
}
}
return dp[s1][s2];
}
};
LeetCode 121. 买卖股票的最佳时机
不断更新最低价格。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
int min_price = prices[0];
int profit = 0;
for (int i = 1; i < len; i++) {
if (prices[i] < min_price) {
min_price = prices[i];
} else {
profit = max(prices[i] - min_price, profit);
}
}
return profit;
}
};
LeetCode 122. 买卖股票的最佳时机 II
不断累加上升区间。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
int ans = 0;
for (int i = 0; i < len - 1; i++) {
if (prices[i] < prices[i + 1]) {
ans += prices[i + 1] - prices[i];
}
}
return ans;
}
};
LeetCode 123. 买卖股票的最佳时机 III
以价格数组为[2, 3, 4, 2, 1, 2, 3, 2, 4]为例。
状态机模型如下图:
第一天是no state。
如果只看前4天,树高度为4,从根节点到叶子节点有8种不同的买卖方式,如下图所示:
每个节点的数据结构中需要存储以下三种信息:
#当前是第几天:*dayx*
#当前状态:no state / bought state,换句话说,是否持有股票,状态用0/1表示
#交易次数:已经交易了几次,状态用0/1/2表示
也可以发现每一层节点,它的状态没有什么规律可言,节点存储的信息自然也就不同,所以不属于重叠子问题。
可以用一张 map 表存储第 n 天,持有/不持有股票,完成若干比交易时的最大利润。
表中 key 由三个维度组成,其实就是节点信息
key = day(n) + bought(0/1) + transaction(0/1/2)
value = Max Profit Calculated
从 mem[n][2][3] 中取值时间复杂度是O(1)。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
// 三维状态表示:第i天,j表示是否持有股票,k表示交易完成次数
int dp[n][2][3];
dp[0][0][0] = 0;
dp[0][0][1] = 0;
dp[0][0][2] = 0;
dp[0][1][0] = - prices[0];
dp[0][1][1] = - prices[0];
dp[0][1][2] = - prices[0];
for(int i = 1; i < n; i++) {
dp[i][0][0] = 0;
dp[i][0][1] = max(dp[i - 1][1][0] + prices[i], dp[i - 1][0][1]);
dp[i][0][2] = max(dp[i - 1][1][1] + prices[i], dp[i - 1][0][2]);
dp[i][1][0] = max(dp[i-1][0][0]-prices[i], dp[i-1][1][0]);
dp[i][1][1] = max(dp[i-1][0][1]-prices[i], dp[i-1][1][1]);
dp[i][1][2] = max(dp[i-1][0][2]-prices[i], dp[i-1][1][2]);
}
return dp[n-1][0][2];
}
};
LeetCode 312. 戳气球
以给定数组为:
nums = [3, 5, 8]
进行分析
为了计算方便,先在 nums 数组两侧各添加一个“1”
nums’ = 1, [3, 5, 8], 1
更具一般性的,假设我先戳破 5。那么该数组被划分成了两部分,分别是[1, 3]和[8, 1],由于戳破了5,得到了3 * 5 * 8 枚硬币。接下来如果能够独立的解决[1, 3]和[8, 1]形成的两个子问题,就皆大欢喜,但是这两个数组并不独立,比如戳破 8 时,需要用到3;戳破 3 时需要用到 8。
我们希望沿着上面的思路,可以将一个问题拆分为子问题。
这题不妨将最先戳破 5 换成是最后一个戳破 5,那就说明了戳破 5 之前一步,仅遗留[1, 5, 1],3 和 8不发生关系,被 5 隔断开。该问题求解变成了:
1, [3, 5, 8], 1最后戳破 5,最大硬币数 = 1, [3], 5最大硬币数 + 1 * 5 * 1 + 5, [8], 1最大硬币数。(中括号内的气球才能戳破)
1, [3], 5最大硬币数,只能戳破 3,得到1 * 3 * 5;[5, 8, 1]最大硬币数,只能戳破 8,得到 5 * 8 * 1。
上面是在最后戳破 5 的情况下,只得到唯一答案 60。
如果1, [3, 5, 8], 1最后戳破 3,该问题求解变成了:
1, [3, 5, 8], 1最后戳破 3,最大硬币数 = 1, [nil], 3 最大硬币数 + 1 * 3 * 1 + 3, [5, 8], 1最大硬币数
1, [nil], 3 最大硬币数,没有气球可供戳破了。
3, [5, 8],1最大硬币数,可以选择最后一个戳破 5,也可以选择最后一个戳破 8。
如果 3, [5, 8],1 最后戳破 5,该问题求解变成了:
3, [5, 8],1最后戳破5,最大硬币数 = 3, [nil], 5 最大硬币数 + 3 * 5 * 8 + 5, [8], 1最大硬币数,而5, [8], 1已经是最小的子问题了,即递归基,此时只能戳破 8,得到 40 枚硬币。
class Solution {
public:
int maxCoins(vector<int>& nums) {
int n = nums.size();
vector<int> val(n + 2);
val[0] = val[n + 1] = 1;
for (int i = 1; i <= n; i++) {
val[i] = nums[i - 1];
}
vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));
for (int window_size = 1; window_size <= n; window_size ++) {
for (int left = 1; left <= n - window_size + 1; left ++) {
int right = left + window_size - 1;
for (int i = left; i <= right; i++) {
dp[left][right] = max((dp[left][i - 1] +
val[left - 1] * val[i] * val[right + 1] +
dp[i + 1][right]),
dp[left][right]);
}
}
}
return dp[1][n];
}
};
LeetCode 91. 解码方法
这题就是斐波那契数列的变种,无非要考虑一些情况。
比如字符串“128207”
从左往右,每次取的数字,单个的情况是否合法?和前一个数字组合成的新数字是否合法?是需要我们判断的。
比如取到 2,2自身合法,和前一个 1 组成的 12 也合法;
比如取到8,8自身合法,但和前一个2 组成的 28 非法。
比如取到0,0自身非法,但和前一个2 组成的 20 合法。
比如取到7,7自身合法,但和前一个0 组成的 07 非法。
class Solution {
public:
int numDecodings(string s) {
int n = s.size();
if (n == 0 || s[0] == '0') {
return 0;
}
if (n == 1) {
return 1;
}
vector<int> dp(n + 1);
dp[0] = 1; // "空串"
dp[1] = 1; // 仅一个数字
for (int i = 2; i <= n; i++) {
int digit_first = s[i - 2] - '0';
int digit_second = s[i - 1] - '0';
int digit = digit_first * 10 + digit_second;
if (digit_second != 0) {
dp[i] = dp[i - 1];
}
if (digit_first != 0 && 1 <= digit && digit <= 26) {
dp[i] += dp[i - 2];
}
}
return dp[n];
}
};
LCR 003. 比特位计数
十进制 | 二进制 |
---|---|
689 | 0010 1011 0001 |
688 | 0010 1011 0000 |
344 | 0001 0101 1000 |
十进制 | 二进制 |
---|---|
9 | 1001 |
8 | 1000 |
4 | 0100 |
class Solution {
public:
vector<int> countBits(int n) {
vector<int> bits(n + 1);
for (int i = 1; i <= n; i ++)
{
bits[i] = bits[i >> 1] + (i % 2);
}
return bits;
}
};