1339.分裂二叉树的最大乘积
题目:
给你一棵二叉树,它的根为 root 。请你删除 1 条边,使二叉树分裂成两棵子树,且它们子树和的乘积尽可能大。
由于答案可能会很大,请你将结果对 10^9 + 7 取模后再返回。
示例 1:
输入:root = [1,2,3,4,5,6]
输出:110
解释:删除红色的边,得到 2 棵子树,和分别为 11 和 10 。它们的乘积是 110 (11*10)
示例 2:
输入:root = [1,null,2,3,4,null,null,5,6]
输出:90
解释:移除红色的边,得到 2 棵子树,和分别是 15 和 6 。它们的乘积为 90 (15*6)
示例 3:
输入:root = [2,3,9,10,7,8,6,5,4,11,1]
输出:1025
示例 4:
输入:root = [1,1]
输出:1
分析题目之后,要得到分裂二叉树的最大乘积,必须遍历所有遍历所有节点。而分裂之后的二叉树,必定是原二叉树的子树,这就将问题化为求二叉树的两个子树的最大乘积。
遍历二叉树的四种遍历方法中,先遍历子树再遍历根节点的算法只有后序遍历,因此使用后序遍历来遍历该二叉树,并将各子树的和存储在一个数组res中。
在求得各子树的和之后,将所有可能的两棵子树和与根节点的值相乘取最大值。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<long> sums;
static const int mod = 1e9 + 7;
int maxProduct(TreeNode* root) {
postOrder(root);
long res = 0;
for (int i = 0; i < sums.size() - 1; ++i) {
// The last num of array sums is the sum of whole tree.
// sum.back()-sum[i] means the another part of the separated tree by deleting side-i
res = max(res, sums[i] * (sums.back() - sums[i]));
}
return (int)(res % mod);
}
long postOrder(TreeNode* root) {
if(!root){
return 0;
}
long res = root->val + postOrder(root->left) + postOrder(root->right); // The Sum of value in the child tree
sums.push_back(res); // restore the Sum
return res;
}
};
买卖股票的最佳时机(简单)
题目:
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
两次遍历法,指针法等都可以解决这道题,但如何使用动态规划解决类似的股票问题,参考一个方法团灭 6 道股票问题 写下这道题解。
动态规划法的核心是求出状态转移方程以及边界问题,但这两个问题都是那么容易解决。在一拿到这种题目时,将其化简为求后序数字对之前数字的最大差值,如何将每个数字的状态存储对应和表示就成了第一个要解决的问题。
每一个日期有两个选择,买或者卖,用一个大小为2的数组表示,下标为[0]表示买,下标为[1]表示卖,而总计有N个日期,因此使用二维数组就为 dp[N][2] ,其中N-1的下标表示第N-1天,[N-1][0] 表示最后一天购买股票所得到的收益,[N-1][1] 表示最后一天卖出股票所得到的收益。很明显,最后一天买入的股票不会带来任何收入,因此返回结果时只需考虑最后一天卖出的股票收益。
这样就可以得到状态方程:
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]);
dp[i][1] = max(dp[i-1][1], -princes[i]);
当i == 0时会发生边界问题,因此提前判断边界条件:
if i == 0 将dp[i][0] = 0, dp[i][0] = -princes[i];
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.empty()){
return 0;
}
vector<vector<int>> dp(prices.size(), vector<int>(2, 0));
for(int i = 0; i < prices.size(); ++i){
if(!i){
// i == 0
dp[i][0] = 0;
dp[i][1] = -prices[i];
continue;
}
// Action sell means gaining money, for which, the best way for transaction is
// comparing the income yesterday and today, then select the max income as
// today's income.
// dp[i-1][1] means the stock bought in yesterday, it's throwing money away,
// dp[i-1][1]+prices[i] means the income we can gain if the stock is sold in terday.
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]);
// Action buy means throwing away the profit we own now
dp[i][1] = max(dp[i-1][1], -prices[i]);
}
return dp[prices.size()-1][0];
}
};
1277.统计全为 1 的正方形子矩阵(中等)
题目:
给你一个 m * n 的矩阵,矩阵中的元素不是 0 就是 1,请你统计并返回其中完全由 1 组成的 正方形 子矩阵的个数。
示例 1:
输入:matrix =
[
[0,1,1,1],
[1,1,1,1],
[0,1,1,1]
]
输出:15
解释:
边长为 1 的正方形有 10 个。
边长为 2 的正方形有 4 个。
边长为 3 的正方形有 1 个。
正方形的总数 = 10 + 4 + 1 = 15.
示例 2:
输入:matrix =
[
[1,0,1],
[1,1,0],
[1,1,0]
]
输出:7
解释:
边长为 1 的正方形有 6 个。
边长为 2 的正方形有 1 个。
正方形的总数 = 6 + 1 = 7.
class Solution {
public:
int countSquares(vector<vector<int>>& matrix) {
if(matrix.empty()){
return 0;
}
int row = matrix.size(), column = matrix.back().size();
vector<vector<int>> dp(row, vector<int>(column, 0));
int numOfSquare = 0;
for(int i = 0; i < row; i++){
for(int j = 0; j < column; j++){
if(i == 0 || j == 0){
dp[i][j] = matrix[i][j];
}
else if(matrix[i][j] == 0){
dp[i][j] = 0;
}
else{
dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
}
numOfSquare += dp[i][j];
}
}
return numOfSquare;
}
};
221.最大正方形
题目:
在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
示例:
输入:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
输出: 4
暴力法已不再多提,
当从左上角的 [0][0] 开始遍历整个矩阵,若在一次遍历内解决问题则必须记录此前的状态。通过对比下标 [2][2] 与下标 [2][3] 元素,可以化简为使用对角线右下角元素来判断正方形的面积。
使用动态规划的步骤:
- 将问题划分为子问题求解; 若要求的最大的正方形,则需求的各小正方形中的最大一个
- 设立状态转移方程;
dp(i, j)=min(dp(i−1, j), dp(i−1, j−1), dp(i, j−1))+1 - 边界条件;数组遍历结束。
很明显,判断下标为(i, j)的元素的正方形大小时需要得到 (i-1, j) , (i-1, j-1), (i, j-1)的正方形面积, 因此将 dp数组row + 1, column + 1;
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
if(matrix.empty()){
return 0;
}
int row = matrix.size(), column = matrix.back().size();
vector<vector<int>> dp(row+1, vector<int>(column+1, 0));
int maxSide = 0;
for(int i = 1; i <= row; i++){
for(int j = 1; j <= column; j++){
if(matrix[i-1][j-1] == '1'){
// matrix[i-1][k-1] is '1', the request to constructing a square is settled.
dp[i][j] = min(min(dp[i][j-1], dp[i-1][j]), dp[i-1][j-1]) + 1;
// Get the max side of square.
maxSide = max(maxSide, dp[i][j]);
}
}
}
// Area
return maxSide*maxSide;
}
};