[leetcode ] 5.29week | dp | 组合数学 | 图 | 打家劫舍
字典序 直接调用的 API
WA 了一次,注意==1 的鲁棒性
代码:
class Solution {
public:vector<int> maxTargetNodes(vector<vector<int>>& edges1, vector<vector<int>>& edges2, int k) {int n1 = edges1.size(), n2 = edges2.size();int n = 0, m = 0;for(auto c:edges1){n = max(max(c[0],c[1]),n);}for(auto c:edges2){m = max(max(c[0],c[1]),m);}vector<vector<int>>xy1(n+1), xy2(m+1);for(auto c:edges1){int a = c[0], b = c[1];xy1[a].push_back(b);xy1[b].push_back(a);}for(auto c:edges2){int a = c[0], b = c[1];xy2[a].push_back(b);xy2[b].push_back(a);}vector<int>sum1(n+1,0), sum2(m+1,0);int mx1 = 0, mx2 = 0;for(int i=0;i<=n;i++){queue<int>qu;int k_ = k;int sum = 1;if(k_==1)mx1 = max(mx1,sum);qu.push(i);vector<bool>st(n+1,true);st[i]=false;while(k_>0){k_--;int sz = qu.size();while(sz>0){sz--;int tp = qu.front();qu.pop();for(auto c:xy1[tp]){if(st[c]==true){ qu.push(c);st[c]=false;}}}sum += qu.size();if(k_==1)mx1 = max(mx1,sum);}sum1[i] = sum;}for(int i=0;i<=m;i++){queue<int>qu;int k_ = k;int sum = 1;if(k_==1)mx2 = max(mx2,sum);qu.push(i);vector<bool>st(m+1,true);st[i]=false;while(k_>0){k_--;int sz = qu.size();while(sz>0){sz--;int tp = qu.front();qu.pop();for(auto c:xy2[tp]){if(st[c]==true){ qu.push(c);st[c]=false;}}}sum += qu.size();if(k_==1)mx2 = max(mx2,sum);}sum2[i] = sum;}vector<int>ans(n+1,0);for(int i=0;i<=n;i++){ans[i] = sum1[i] + mx2;}return ans;}
};
🌟题目大意(简化版)
我们有两棵树(Tree 1 和 Tree 2),每棵树由边列表给出。我们要做的是:
- 把 Tree 1 的每个节点 连接到 Tree 2 的某个节点;
- 然后从这个连接出发,在一定限制下(比如最多走几步)能到达多少个目标节点;
- 我们希望找出:对于 Tree 1 中的每一个节点,它最多能连接到多少个目标节点。
💡核心思路(白话讲解)
第一步:树是“国际象棋棋盘”
我们可以把一棵树上的所有节点想象成像国际象棋棋盘一样:
- 相邻节点颜色不同(黑→白→黑……)
- 这样就形成了两个组:
-
- 组0:距离根节点(比如节点0)为偶数步的节点
- 组1:距离根节点为奇数步的节点
这就叫做按奇偶分层,用 DFS 或 BFS 就能做到。
第二步:找第二棵树的最大潜力
对 Tree 2:
- 我们把它也分成两个组(组0 和 组1);
- 看哪一组节点更多,我们就选那一组作为通用连接对象;
- 记录最大值
max2 = max(组0数量, 组1数量)
第三步:给第一棵树的每个节点算答案
对 Tree 1:
- 每个节点属于组0或组1;
- 它的目标节点只能在它自己这一组里;
- 再加上 Tree 2 的最大组数量(
max2
); - 所以答案就是:自己的组大小 + max2
🧠举个例子(帮助理解)
假设 Tree 1 分成了:
- 组0 有 5 个节点
- 组1 有 3 个节点
Tree 2 分成了:
- 组0 有 4 个节点
- 组1 有 6 个节点 → 最大是 6
那么:
- Tree 1 中属于组0的节点的答案是:5 + 6 = 11
- 属于组1的节点的答案是:3 + 6 = 9
总结
- 树是“没有环”的结构,可以 DFS/BFS 遍历;
- 树一定是“二分图”,可以用黑白染色;
- 染色后就可以按颜色(奇偶)分组统计;
- 答案构造很简单:自己组的大小 + 别人最大的组大小。
class Solution {
public:vector<int> maxTargetNodes(vector<vector<int>>& edges1, vector<vector<int>>& edges2) {auto count = [](vector<vector<int>>& edges) {vector<vector<int>> g(edges.size() + 1);//把边列表转换成邻接表(也就是一个图的结构,方便遍历)for (auto& e : edges) {int x = e[0], y = e[1];g[x].push_back(y);g[y].push_back(x);}//用 DFS 遍历整棵树,统计每个节点离根节点的距离是奇数还是偶数,分别计数array<int, 2> cnt{};auto dfs = [&](this auto&& dfs, int x, int fa, int d) -> void {cnt[d]++;for (int y : g[x]) {if (y != fa) {dfs(y, x, d ^ 1);}}};dfs(0, -1, 0);return pair(g, cnt);};//统计 Tree 2 的两个组的大小,取最大值作为通用连接对象auto [_, cnt2] = count(edges2);int max2 = max(cnt2[0], cnt2[1]);//统计 Tree 1 的两个组的大小,先让每个节点带上 Tree 2 的最大值auto [g, cnt1] = count(edges1);vector<int> ans(g.size(), max2);//再次 DFS 遍历 Tree 1,根据当前节点属于哪个组,加上它自己这组的数量,最终得到答案auto dfs = [&](this auto&& dfs, int x, int fa, int d) -> void {ans[x] += cnt1[d];for (int y : g[x]) {if (y != fa) {dfs(y, x, d ^ 1);}}};dfs(0, -1, 0);return ans;}
};
枚举第一个小朋友拿的糖果数量,再结合剩下糖果在另外两个小朋友之间的合法分配方式,最终统计所有可能的分法总数
if (left > 2 * limit) continue;if (limit >= left) res += left + 1;else res += (limit - (left - limit)) + 1;
简单组合数学知识:
可能情况总数 = (上限 - 下限) + 1
class Solution {
public:long long distributeCandies(int n, int limit) {long long res = 0;for (int i = 0; i <= min(limit, n); ++i) {// 第一个人拿了 i 个糖果int left = n - i;// 剩下的两个孩子每人最多 limit,总共最多 2 * limitif (left > 2 * limit) {continue;}if (limit >= left) {res += left + 1;} else {// 可能情况总数 = (上限 - 下限) + 1res += (limit - (left - limit)) + 1;}}return res;}
};
dp依照三个位置
“每个人要比左右评分更高的孩子拿到更多的糖果” → 从左到右、从右到左各扫一遍填表。
- 感觉就是爬坡。先全部设置为最小值1
- 正向遍历,如果相邻的比自己大就加1
- 反向遍历,如果相邻的比自己大也加1,取最大值
代码
class Solution {
public:int candy(vector<int>& ratings) {int n = ratings.size();vector<int> dp(n, 1); // 初始每人至少一颗糖// 从左往右看,确保评分更高的孩子糖果也更多for(int i = 1; i < n; i++){if(ratings[i] > ratings[i - 1])dp[i] = dp[i - 1] + 1;}// 从右往左看for(int i = n - 2; i >= 0; i--){if(ratings[i] > ratings[i + 1])dp[i] = max(dp[i], dp[i + 1] + 1);}int total = 0;for(int d : dp)total += d;return total;}
};
打家劫舍
二维 dp 来实现选择,选 A or 选 B
代码:
class Solution {
public:long long maxEnergyBoost(vector<int>& A, vector<int>& B) {int n = A.size();long long f[n][2];f[0][0] = A[0]; f[0][1] = B[0];for (int i = 1; i < n; i++) {f[i][0] = max(f[i - 1][0] + A[i], f[i - 1][1]); //刚换过来的 当前步是不能喝的f[i][1] = max(f[i - 1][1] + B[i], f[i - 1][0]);}return max(f[n - 1][0], f[n - 1][1]);}
};