lc865
pair<int, TreeNode*> dfs
计算每个节点左右子树高度,若左右子树高度相同则当前节点是最深节点的最近公共祖先++return {left_height + 1, node};++// 一样高,否则取高度更高子树的祖先
最终返回包含所有最深节点的最小子树的根节点

class Solution {
pair<int, TreeNode*> dfs(TreeNode* node) {
if (node == nullptr)
return {0, nullptr};
auto [left_height, left_lca] = dfs(node->left);
auto [right_height, right_lca] = dfs(node->right);
if (left_height > right_height) { // 左子树更高
return {left_height + 1, left_lca};
}
if (left_height < right_height) { // 右子树更高
return {right_height + 1, right_lca};
}
++return {left_height + 1, node};++ // 一样高
}
public:
TreeNode* subtreeWithAllDeepest(TreeNode* root) {
return dfs(root).second;
}
};
lc902
++数位dp 一种别人设计好了的状态转移套路++
- init:
将数字n转为字符串s(便于逐位处理)
- dfs:参数:
当前处理位i、是否受原数限制is_limit、是否已构成合法数字is_num,返回当前状态下的合法方案数
- 终止条件处理:
若遍历完所有位(i==m),返回is_num(1表示合法方案,0表示无效前导零情况);
- 状态枚举与递归:
先处理"跳过当前位"的无数字情况,再确定当前位可填数字上限,遍历给定数字集合中不超过上限的数字,递归计算下一位结果并累加;
- 记忆化:若当前状态不受原数限制且已构成合法数字,将结果存入dp供后续复用
dfs(第0位、受限状态、++未构成数字开始)++
class Solution {
public:
int atMostNGivenDigitSet(vector<string> &digits, int n) {
auto s = to_string(n);
int m = s.length(), dp[m];
memset(dp, -1, sizeof(dp));
function<int(int, bool, bool)> f = [&](int i, bool is_limit, ++bool is_num++ ) -> int {
++if (i == m) return is_num;++ // 如果填了数字,则为 1 种合法方案
if (!is_limit && ++is_num++ && dp[i] >= 0) return dp[i]; // 在不受到任何约束的情况下,返回记录的结果,避免重复运算
int res = 0;
++if (!is_num)++ // 前面不填数字,那么可以跳过当前数位,也不填数字
// is_limit 改为 false,因为没有填数字,位数都比 n 要短,自然不会受到 n 的约束
// is_num 仍然为 false,因为没有填任何数字
res = f(i + 1, false, ++false);++
++char up = is_limit ? s[i] : '9';++ // 根据是否受到约束,决定可以填的数字的上限
// 注意:对于一般的题目而言,如果这里 is_num 为 false,则必须从 1 开始枚举,由于本题 digits 没有 0,所以无需处理这种情况
for (auto &d : digits) {
// 枚举要填入的数字 d
if (d[0] > up) break; // d 超过上限,由于 digits 是有序的,后面的 d 都会超过上限,故退出循环
// is_limit:如果当前受到 n 的约束,且填的数字等于上限,那么后面仍然会受到 n 的约束
// is_num 为 true,因为填了数字
res += f(i + 1, is_limit && d[0] == up, true);
}
if (!is_limit && is_num) dp[i] = res; // 在不受到任何约束的情况下,记录结果
return res;
};
return f(0, true, ++false);++
}
};
lc1648
二分确定最高售价阈值
先对++高于阈值的球按等差数列求和计算收益,++再用阈值价格补齐剩余订单
最终得到最大利润
typedef long long ll;
class Solution {
private:
static constexpr int MOD = 1e9 + 7;
public:
int maxProfit(vector<int>& inventory, int orders) {
//每次都买剩余数量最大的一筐
//二分球卖出的价格阈值k
//需要注意不一定所有的球都按照阈值出售, 优先二分大于k的球
//如果卖出的数量不足orders, 则按照阈值k出售剩下的球
//用户只想买orders个球
int total = 0;
int lhs = 1, rhs = *max_element(inventory.begin(), inventory.end());
auto check = [&](int k) -> bool{
int sales = 0;
//优先卖出价格大于k的球
for (int x: inventory){
sales += max(x - k, 0);
if (sales > orders)
return true;
}
return false;
};
//lhs使得卖出的球数量小于等于orders
while (lhs <= rhs){
int mid = lhs + (rhs - lhs) / 2;
++if (check(mid))++
lhs = mid + 1;
else
rhs = mid - 1;
}
ll res = 0;
for (int x: inventory){
if (x > lhs){
//等差数列求和 (首项 + 末项)* 项数 / 2
++res += ((ll)(x - lhs) * (ll)(x + lhs + 1)) / 2;++
res %= MOD;
orders -= x - lhs;
}
}
//如果有剩余的order, 则一定按照阈值k的价格出售
res += (ll)lhs * orders;
res %= MOD;
return res;
}
};
lc1992
BFS遍历农田矩阵,找到每块连续农田的左上角起点后,扩展找到其右下角终点,收集所有农田的起点-终点坐标组返回
class Solution
{
public:
const int dirs[4][2] {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
vector<vector<int>> land;
int Row;
int Col;
pair<int, int> bfs(int sr, int sc)
{
int r2 = sr;
int c2 = sc;
queue<pair<int, int>> q;
q.push({sr, sc});
while (!q.empty())
{
auto [r, c] = q.front(); q.pop();
r2 = max(r2, r);
c2 = max(c2, c);
for (int di = 0; di < 4; di ++)
{
int dr = dirs[di][0], dc = dirs[di][1];
int nr = r + dr, nc = c + dc;
if (0 <= nr && nr < Row && 0 <= nc && nc < Col && land[nr][nc] == 1)
{
++land[nr][nc] = 0;
q.push({nr, nc});++
}
}
}
return pair<int, int>{r2, c2};
}
vector<vector<int>> findFarmland(vector<vector<int>>& land)
{
this->land = land;
Row = (int)land.size();
Col = (int)land[0].size();
vector<vector<int>> res;
for (int r = 0; r < Row; r ++)
{
for (int c = 0; c < Col; c ++)
{
if (this->land[r][c] == 1)
{
this->land[r][c] = 0;
++auto [r2, c2] = bfs(r, c);
res.push_back(vector<int>{r, c, r2, c2});++
}
}
}
return res;
}
};