【600】 Non-negative Integers without Consecutive Ones

【629】 K Inverse Pairs Array

【638】 Shopping Offers

【639】 Decode Ways II

【646】 Maximum Length of Pair Chain

【647】 Palindromic Substrings

给定一个字符串,判断有多少子串是 palindromic 的,子串就算所有字母都相同,但是开始和结束位置的下标不同,也算不同的子串。

题解:dp, 时间复杂度是 O(N^2),dp[i][j]  代表 s[i..j] 是不是 回文串。

 class Solution {
public:
int countSubstrings(string s) {
const int n = s.size();
vector<vector<int>> dp(n, vector<int>(n, ));
int res = ;
for (int i = ; i < n; ++i) {
dp[i][i] = ;
res++;
if (i + < n && s[i] == s[i+]) {
dp[i][i+] = ;
res++;
}
}
for (int i = n - ; i >= ; --i) {
for (int j = i + ; j < n; ++j) {
if (dp[i+][j-] == && s[i] == s[j]) {
dp[i][j] = ;
res++;
}
}
}
return res;
}
};

【650】 2 Keys Keyboard (2019年2月20日,谷歌tag,M)

假设你有个键盘,上面只有两个操作:

(1) copy all: You can copy all the characters present on the notepad (partial copy is not allowed).

(2) paste: You can paste the characters which are copied last time.

问一开始给你一个 A, 问复制到 n 个A至少需要几步?

题解:dp[i] 表示复制到 i 个A 至少需要几步。 dp[i] = max(dp[k] + 1 + (i / k - 1)) 其中 i 能够整除 k。

 class Solution {
public:
int minSteps(int n) {
//dp[i] 代表生成 i 个A,需要的最少步数。
vector<int> dp(n+, INT_MAX);
dp[] = , dp[] = ;
for (int i = ; i <= n; ++i) {
for (int k = ; k < i; ++k) {
if (i % k == ) {
int times = (i - k) / k;
dp[i] = min(dp[i], dp[k] + + times);
}
}
}
return dp[n];
}
};

【651】 4 Keys Keyboard (2019年2月20日,谷歌tag,M)

假设你有一个特殊键盘,只有四个键。

Key 1: (A): Print one 'A' on screen.

Key 2: (Ctrl-A): Select the whole screen.

Key 3: (Ctrl-C): Copy selection to buffer.

Key 4: (Ctrl-V): Print buffer on screen appending it after what has already been printed.

你只能按下 N 次键盘,问你能获得最长的 A 是多长。

Input: N = 7

Output: 9

Explanation:

We can at most get 9 A's on screen by pressing following key sequence:

A, A, A, Ctrl A, Ctrl C, Ctrl V, Ctrl V

题解:通过观察例子我们可以发现,我们令 dp[i] 代表按下 i 个键之后的最长的字符串的长度,转移方程: 如果第 i 个键按下的是key1的话,dp[i] = dp[i-1] + 1,

如果第 i 个键按下的是 key4 的话,我们需要从前面找到一个键(假设是第 k 个键),然后从这个键开始,重复key2, key3, key4,key4,key4...... 所以我们这个时候,dp[i] = max(dp[k] * (x-k-1)) (1 <= k < x)

所以通过这 2-for loop 就可以解决这个问题。时间复杂度是O(N^2)

 class Solution {
public:
/*
dp[0] = 0;
dp[1] = 1;
dp[2] = 2;
dp[3] = 3;
dp[4] = 4;(2)
dp[5] = 5;(4,3)
dp[6] = 6;(6,4
dp[7] = 5, 8, 9
*/
int maxA(int N) {
vector<int> dp(N+, );
for (int i = ; i <= N; ++i) {
dp[i] = i;
for (int j = ; j < i; ++j) {
dp[i] = max(dp[j] * (i - j - ), dp[i]);
}
}
return dp[N];
}
};

【656】 Coin Path

【664】 Strange Printer (2019年4月9日)

There is a strange printer with the following two special requirements:

  1. The printer can only print a sequence of the same character each time.
  2. At each turn, the printer can print new characters starting from and ending at any places, and will cover the original existing characters.

Given a string consists of lower English letters only, your job is to count the minimum number of turns the printer needed in order to print it.

Example 1:
Input: "aaabbb"
Output: 2
Explanation: Print "aaa" first and then print "bbb".
Example 2:
Input: "aba"
Output: 2
Explanation: Print "aaa" first and then print "b" from the second place of the string, which will cover the existing character 'a'.

题解:本题用dp做。dp[i][j] 表示打印 s[i] 到 s[j] 的最短打印的次数。

转移方程为: dp[i][j] = min(dp[i][j], temp), 其中,if (s[k] == s[j]) { temp = dp[i][k] + dp[k+1][j] - 1 } else {temp = dp[i][k] + dp[k+1][j]}

 class Solution {
public:
int strangePrinter(string s) {
const int n = s.size();
if (n == ) {return ;}
vector<vector<int>> dp(n, vector<int>(n, INT_MAX));
for (int i = ; i < n; ++i) {
dp[i][i] = ;
if (i+ < n) {
dp[i][i+] = (s[i] == s[i + ]) ? : ;
}
}
for (int len = ; len <= n; ++len) {
for (int i = ; i + len <= n; ++i) {
int j = i + len - ;
dp[i][j] = len;
for (int k = i; k < j; ++k) {
int temp = dp[i][k] + dp[k+][j];
if (s[k] == s[j]) {temp--;}
dp[i][j] = min(dp[i][j], temp);
}
}
}
return dp[][n-];
}
};

【673】 Number of Longest Increasing Subsequence

【688】 Knight Probability in Chessboard

【689】 Maximum Sum of 3 Non-Overlapping Subarrays

【691】 Stickers to Spell Word

【698】 Partition to K Equal Sum Subsets

【712】 Minimum ASCII Delete Sum for Two Strings (2018年12月21日,算法群)

给了两个字符串 s1 和 s2,要从 s1 和 s2 中删除一些字符使得这两个字符串相等。求最小的删除字符的 ACSII 的和。

题解:字符串的编辑距离的变种。dp[i][j] 表示从 s1[0..i-1] 到 s2[0..j-1] 这两个字符串相等所求的最小和。转移方程为: if (s1[i-1] == s2[j-1]) {dp[i][j] = dp[i-1][j-1]}, else {dp[i][j] = min(dp[i-1][j] + (int)s1[i-1], dp[i][j-1] + (int)s2[j-1])}

 class Solution {
public:
int minimumDeleteSum(string s1, string s2) {
const int n1 = s1.size(), n2 = s2.size();
vector<vector<int>> dp(n1 + , vector<int>(n2 + , INT_MAX));
dp[][] = ;
for (int i = ; i <= n1; ++i) {
dp[i][] = dp[i-][] + (int)s1[i-];
}
for (int j = ; j <= n2; ++j) {
dp[][j] = dp[][j-] + (int)s2[j-];
}
for (int i = ; i <= n1; ++i) {
for (int j = ; j <= n2; ++j) {
if (s1[i-] == s2[j-]) {
//dp[i][j] = min(dp[i][j], min(dp[i-1][j] + (int)s1[i-1], dp[i][j-1] + (int)s2[j-1]));
dp[i][j] = min(dp[i][j], dp[i-][j-]);
} else {
dp[i][j] = min(dp[i][j], min(dp[i-][j] + (int)s1[i-], dp[i][j-] + (int)s2[j-]));
}
}
}
return dp[n1][n2];
}
};

【714】 Best Time to Buy and Sell Stock with Transaction Fee (算法群 2018年10月22日题目)

还是股票买卖的相关问题,给了一个股票价格的数组,prices[i] 代表第 i 天的股票价格,每次卖出要交手续费,问最后的max profit 是多少。

题解:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/discuss/108892/Java-DP-solution-O(n)-to-O(1)-space

这题可以用滚动数组(两个变量优化成O(1)的空间复杂度),但是优化后的不好理解,而且这种优化也不算什么高端技巧。所以我们这里先讨论下这题 dp 的本质。

我们第 i 天要么持有股票,要么不持有股票。 我们用 hold[i] 表示第 i 天持有股票的 max profit, unhold[i] 表示第 i 天不持有股票的 max profit。

那么转移方程可以这么理解: 我们在第 i 天有两种情况,要么我们持有股票,要么我们不持有股票。

1. 第 i 天持有股票的情况下, hold[i] = max(hold[i-1], unhold[i-1] - prices[i])  //意思是说我们要么第 i-1 天就持有股票, 第 i 天啥也不干; 要么我们在第 i 天买了股票

2.第 i 天不持有股票的情况下, unhold[i] = max(unhold[i-1], hold[i-1] + prices[i] - fee) //意思是说我们 第 i-1 天就不持有股票了,第 i 天啥也不干; 要们我们在第 i 天卖了股票

现在的时间复杂度是O(N), 空间复杂度是O(1)。可以用两个变量优化两个数组。

 class Solution {
public:
//hold[i] represents max profit of holding stock until day i
//nothold[i] represents max profit of not holding stock until day i
//transaction function: for each day i, we have 2 situations.
//1. hold stock in day i (you can either do nothing in day i or buy stock int day i)
// hold[i] = max(hold[i-1], nothold[i-1]-price[i])
//2. not hold stock in day i (you can either do nothing in day i or sell stock in day i)
// nothold[i] = max(nothold[i-1], hold[i-1] + price[i] - fee) int maxProfit(vector<int>& prices, int fee) {
const int n = prices.size();
if (n <= ) { return ; }
vector<int> hold(n, ), unhold(n, );
hold[] = -prices[], unhold[] = ;
for (int i = ; i < n; ++i) {
hold[i] = max(hold[i-], unhold[i-] - prices[i]);
unhold[i] = max(unhold[i-], hold[i-] + prices[i] - fee);
}
return unhold[n-];
}
};

【718】 Maximum Length of Repeated Subarray (2019年3月29日)

给定两个数组,返回他们最长的相同子数组的长度。

Example 1:
Input:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
Output: 3
Explanation:
The repeated subarray with maximum length is [3, 2, 1].

题解:我一开始对example的例子理解有问题,我一直以为是A的前三个元素和B的前三个元素能形成 repeated subarray。但是其实不是这样的,其实是 A 的后三个元素的[3,2,1] 和 B 的前三个元素的[3,2,1] 形成了一个 repeated subarray。

所以dp的定义和转移方程这样看也就比较明确了,dp[i][j] 代表 包含 A[i-1] 和 B[j-1]  的最长的 repeated subarray 的长度。

所以转移方程是:  if (A[i-1] == B[j-1]) { dp[i][j] = dp[i-1][j-1] + 1; }

 class Solution {
public:
int findLength(vector<int>& A, vector<int>& B) {
int lenA = A.size(), lenB = B.size();
vector<vector<int>> dp(lenA+, vector<int>(lenB+, ));
int res();
for (int i = ; i <= lenA; ++i) {
for (int j = ; j <= lenB; ++j) {
if (A[i-] == B[j-]) {
dp[i][j] = + dp[i-][j-];
res = max(res, dp[i][j]);
}
}
}
return res;
}
};

【727】 Minimum Window Subsequence

【730】 Count Different Palindromic Subsequences

【740】 Delete and Earn (2019年5月17日,算法群)

给了一个整数数组 nums,操作规则如下:

In each operation, you pick any nums[i] and delete it to earn nums[i] points. After, you must delete every element equal to nums[i] - 1 or nums[i] + 1.

You start with 0 points. Return the maximum number of points you can earn by applying such operations.

Example 1:
Input: nums = [3, 4, 2]
Output: 6
Explanation:
Delete 4 to earn 4 points, consequently 3 is also deleted.
Then, delete 2 to earn 2 points. 6 total points are earned. Example 2:
Input: nums = [2, 2, 3, 3, 3, 4]
Output: 9
Explanation:
Delete 3 to earn 3 points, deleting both 2's and the 4.
Then, delete 3 again to earn 3 points, and 3 again to earn 3 points.
9 total points are earned.

题解:我们可以双状态表示这个题,先做个频率统计,再按照 key 从小到大排序,然后 p 代表算上当前 key 能取得的最大值,q 代表不计算当前的 key 取得的最大值。

状态转移方程如代码所示:

 //time complexity: O(N)
class Solution {
public:
int deleteAndEarn(vector<int>& nums) {
const int n = nums.size();
map<int, int> freq;
for (auto& num : nums) { freq[num]++; }
vector<pair<int, int>> array;
for (auto p : freq) {
array.push_back(p);
}
if (array.empty()) {return ;}
int p = array[].first * array[].second, q = ; //p 代表选了当前这个值, q 代表不选当前这个值
// cout << "p = " << p << " " << "q = " << q << endl;
for (int i = ; i < array.size(); ++i) {
int temp_p = p, temp_q = q;
if (array[i].first != array[i-].first + ) { //can take array[i]
p = max(temp_p, temp_q) + array[i].first * array[i].second;
q = max(temp_p, temp_q);
} else {
p = temp_q + array[i].first * array[i].second;
q = max(temp_p, temp_q);
}
}
return max(p, q);
}
};

【741】 Cherry Pickup (2019年1月12日,算法群)

今天心情很差,抄的答案==,有点upset

【746】 Min Cost Climbing Stairs

【750】 Number Of Corner Rectangles

【764】 Largest Plus Sign

【787】 Cheapest Flights Within K Stops(2019年2月9日,谷歌tag)

给了一个有向图,边上有权重,问从 src 到 dst 最多经过 K 个站的最少花费。

题解:dp[k][v] 表示从 src 开始到 v 经过 k 个站的最小花费。dp[k][v] = min(dp[k][v], dp[k-1][u] + cost[u][v])。时间复杂度是 O(K*n*n)

初始化 cost[u][v] 表示从 u 到 v 的花费。(边上的权重)dp[0][src] = 0。dp[0][v] = cost[u][v] (u = src)

 //time complexity: O(k*n*n)
class Solution {
public:
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
vector<vector<int>> dp(K + , vector<int>(n, INT_MAX));
vector<vector<int>> cost(n, vector<int>(n, INT_MAX));
//init
int ret = INT_MAX;
for (auto& f : flights) {
int u = f[], v = f[], w = f[];
if (u == src) {
dp[][v] = w;
if (v == dst) {ret = w;}
}
cost[u][v] = w;
}
for (int k = ; k <= K; ++k) {
for (int d = ; d < n; ++d) {
for (int u = ; u < n; ++u) {
if (cost[u][d] == INT_MAX || dp[k-][u] == INT_MAX) {continue;}
dp[k][d] = min(dp[k][d], dp[k-][u] + cost[u][d]);
}
if (d == dst) { ret = min(ret, dp[k][d]); }
}
}
if (ret == INT_MAX) { ret = -; }
return ret;
}
};

【790】 Domino and Tromino Tiling

【801】 Minimum Swaps To Make Sequences Increasing

【808】 Soup Servings (2019年1月4日,算法群,第一次做,需要复习)

给了 A,B 两种 soup,一开始都有 N ml。有如下四种选择,每种选择的概率都是 25%。

  1. Serve 100 ml of soup A and 0 ml of soup B
  2. Serve 75 ml of soup A and 25 ml of soup B
  3. Serve 50 ml of soup A and 50 ml of soup B
  4. Serve 25 ml of soup A and 75 ml of soup B

如果在配比中有一种soup的剂量不够调一份的,就用剩下所有的调一份。返回 soup A 先空的概率 加上 soup A, B一起空的概率。

题解:我们用 25ml 算一个 serving,先计算A, B每种 soup 的serving。我们用一个记忆化数组来记录算过的概率。

f[a][b] = 0.25 * (f[a-4][b] + f[a-3][b-1] + f[a-2][b-2] + f[a-1][b-3])

 class Solution {
public:
double soupServings(int N) {
const int serving = N / + (N % >= ? : );
if (serving >= ) {
return 1.0;
}
return helper(serving, serving);
}
double helper(int a, int b) {
if (a == && b == ) {
return 0.5;
} else if (a == ) {
return 1.0;
} else if (b == ) {
return 0.0;
}
if (memo.find(a) != memo.end() && memo[a].find(b) != memo[a].end()) {
return memo[a][b];
}
double ret = 0.0;
ret = 0.25 * (helper(max(a-, ), b) + helper(max(a-, ), max(b-, )) + helper(max(a-, ), max(b-, )) + helper(max(a-, ), max(b-, )));
memo[a][b] = ret;
return ret;
}
unordered_map<int, unordered_map<int, double>> memo;
};

【813】 Largest Sum of Averages (2019年5月20日,算法群)(google tag M)

We partition a row of numbers A into at most K adjacent (non-empty) groups, then our score is the sum of the average of each group. What is the largest score we can achieve?

Note that our partition must use every number in A, and that scores are not necessarily integers.

Example:
Input:
A = [9,1,2,3,9]
K = 3
Output: 20
Explanation:
The best choice is to partition A into [9], [1, 2, 3], [9]. The answer is 9 + (1 + 2 + 3) / 3 + 9 = 20.
We could have also partitioned A into [9, 1], [2], [3, 9], for example.
That partition would lead to a score of 5 + 2 + 6 = 13, which is worse.

Note:

1 <= A.length <= 100.
1 <= A[i] <= 10000.
1 <= K <= A.length.
Answers within 10^-6 of the correct answer will be accepted as correct.

题解:dp

dp[k][i] 表示前 i 个元素划分成 k 的最大平均和。

dp[1][i] = avg(sum(a[0] ~ a[i-1]))

dp[k][i] = max(dp[k-1][j] + sum(a[j] ~ a[i]) / (i-j))

time complexity: O(KN^2), space complexity: O(KN)

 //dp 和打气球那题类似
class Solution {
public:
double largestSumOfAverages(vector<int>& A, int K) {
const int n = A.size();
vector<double> _sum(n+, 0.0);
for (int i = ; i < n; ++i) {
_sum[i+] = _sum[i] + A[i];
}
vector<vector<double>> dp(K+, vector<double>(n+, 0.0));
for (int k = ; k <= K; ++k) {
for (int i = ; i <= n; ++i) {
if (k == ) {
dp[k][i] = _sum[i] / i;
} else {
for (int j = ; j < i; ++j) {
dp[k][i] = max(dp[k][i], dp[k-][j] + (_sum[j] - _sum[i]) / (j-i));
}
}
}
}
return dp[K][n];
}
};

【818】 Race Car

【837】 New 21 Game

【838】 Push Dominoes

【847】 Shortest Path Visiting All Nodes

【871】 Minimum Number of Refueling Stops

【873】 Length of Longest Fibonacci Subsequence

【877】 Stone Game

【879】 Profitable Schemes

【887】 Super Egg Drop



【LeetCode】动态规划(下篇共39题)的更多相关文章

  1. Leetcode 简略题解 - 共567题

    Leetcode 简略题解 - 共567题     写在开头:我作为一个老实人,一向非常反感骗赞.收智商税两种行为.前几天看到不止两三位用户说自己辛苦写了干货,结果收藏数是点赞数的三倍有余,感觉自己的 ...

  2. 【LeetCode】回溯法 backtracking(共39题)

    [10]Regular Expression Matching [17]Letter Combinations of a Phone Number [22]Generate Parentheses ( ...

  3. 快速上手leetcode动态规划题

    快速上手leetcode动态规划题 我现在是初学的状态,在此来记录我的刷题过程,便于以后复习巩固. 我leetcode从动态规划开始刷,语言用的java. 一.了解动态规划 我上网查了一下动态规划,了 ...

  4. 剑指offer 面试39题

    面试39题: 题目:数组中出现次数超过一半的数字 题:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中 ...

  5. LeetCode动态规划题总结【持续更新】

    以下题号均为LeetCode题号,便于查看原题. 10. Regular Expression Matching 题意:实现字符串的正则匹配,包含'.' 和 '*'.'.' 匹配任意一个字符,&quo ...

  6. 【LeetCode】堆 heap(共31题)

    链接:https://leetcode.com/tag/heap/ [23] Merge k Sorted Lists [215] Kth Largest Element in an Array (无 ...

  7. 【LeetCode】排序 sort(共20题)

    链接:https://leetcode.com/tag/sort/ [56]Merge Intervals (2019年1月26日,谷歌tag复习) 合并区间 Input: [[1,3],[2,6], ...

  8. 【LeetCode】二叉查找树 binary search tree(共14题)

    链接:https://leetcode.com/tag/binary-search-tree/ [220]Contains Duplicate III (2019年4月20日) (好题) Given ...

  9. 【LeetCode】数学(共106题)

    [2]Add Two Numbers (2018年12月23日,review) 链表的高精度加法. 题解:链表专题:https://www.cnblogs.com/zhangwanying/p/979 ...

随机推荐

  1. UOJ418. 【集训队作业2018】三角形

    http://uoj.ac/problem/418 题解 考虑激活每个节点时,它的每个儿子都是放满的. 那每一次的操作我们可以用一个二元组来表示\((w_i-\sum w_{son},\sum w_{ ...

  2. VMware 15 搭建MacOS 10.14教程

    写于2018.12.23 教程原文链接:https://pan.baidu.com/s/1wvNYg_MQH_lwewKbpCQ5_Q ———————————————————————————————— ...

  3. Dreamweaver cc新版本css单行显示

    新版本通用:(1)C:\Users\admin\Application Data\Adobe\Dreamweaver CC 2018\cloudpref\Adobe Dreamweaver CC 20 ...

  4. navigator组件(相当于a标签)

    navigator组件:页面链接: navigator组件属性: target:类型 字符串 在哪个目标上发生跳转,默认当前小程序 属性值:self 当前小程序 miniProgram 其他小程序 u ...

  5. Linux文件系统启动过程及login的实现

    1. busybox简介 busybox是一个集成了一百多个最常用linux命令和工具的软件,它将许多常用的LINUX命令和工具结合到了一个单独的可执行程序中.虽然与相应的GNU工具比较起来,busy ...

  6. C/C++判断字符串是否包含某个子字符串

    C风格 #include <iostream> #include <string> #include <cstring> using namespace std; ...

  7. set集合 浅层拷贝会和深层拷贝

    一.什么是set集合 集合是无序的,不重复的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的.以下是集合最重要的两点: 1.去重,把一个列表变成集合, ...

  8. EF6中一个关于时间类型 datetime2 的坑

    在一个访问下位机的程序中,返回的时间戳有时候因断线产生0001年01月01日的时间,而原先使用拼接SQL进行数据存储的操作时,这个问题是可以跳过的. 这次把拼接SQL的部分重新改为EF进行管理,这个坑 ...

  9. MVC 源码系列之路由(一)

    路由系统 注释:这部分的源码是通过Refector查看UrlRoutingModule的源码编写,这部分的代码没有写到MVC中,却是MVC的入口. 简单的说一下激活路由之前的一些操作.一开始是由MVC ...

  10. 【ABAP系列】SAP ABAP 利用class创建客户/供应商主数据

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP ABAP 利用class创建 ...