12/04 1038. 从二叉搜索树到更大和树
给定一颗二叉树,将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。
题目链接
上面这张图很清晰,本质是右—中—左的子树遍历加上累计节点值,一次遍历足矣。
class Solution {
public:
//本质是个右——中——左的遍历
TreeNode* bstToGst(TreeNode* root) {
int sum = 0;
return Getval(root, sum);
}
TreeNode* Getval(TreeNode* root, int& sum){
if(root == NULL) return NULL;
root->right = Getval(root->right, sum);
sum += root->val; root->val = sum;
root->left = Getval(root->left, sum);
return root;
}
};
12/05 2477. 到达首都的最少油耗
给定一颗树,每个节点有一辆车(载客量不同),每个城市都有1人要前往0号节点,相邻城市间一辆车通过消耗为1,求最小消耗。
题目链接
题目听起来很复杂,但实际上很简单。
所有节点都有人要到首都,最大消耗为0号节点到其他所有节点路径长之和,而某些节点的路多次通过只收一次钱。
因此,计算出每个节点底下有多少个节点(即有多少人会来到此节点),再看该节点的车运几次能把所有人运到父节点即可。
class Solution {
public:
long long minimumFuelCost(vector<vector<int>>& roads, int seats) {
int n = roads.size()+1;
vector<vector<int>> map(n+1);
vector<int>vis(n+1,0);
for(auto& road: roads){
map[road[0]].push_back(road[1]);
map[road[1]].push_back(road[0]);
}
long long ans = 0;
function<int(int)>DFS = [&](int root) -> int{
int count = 1;
vis[root] = 1;
for(int x: map[root])
if(!vis[x]) count += DFS(x);
if(root) ans += (count-1)/seats+1;
return count;
};
DFS(0);
return ans;
}
};
12/06 2646. 最小化旅行的价格总和
给定一颗树,树上每个节点都有其价格,一条路径的价格总和是所经过所有节点的价格之和;
给定一组旅行的起点与终点,你可以在出发前选择任意多个节点减半其价格,只要这些节点互不相邻。
返回旅行价格最小值。
题目链接
在给定旅行起点终点的情况下,树上所有点的访问次数实际上已经固定。
于是我们可以先使用DFS把点的访问次数count计数,再做树上DP。
树上dp就很简单了,dp[n][0]代表不减半该节点价格时最小值,dp[n][1]代表减半该节点价格时最小值。
转移方程为:
d
p
[
i
]
[
0
]
=
m
i
n
(
d
p
[
f
a
]
[
0
]
,
d
p
[
f
a
]
[
1
]
)
+
(
c
o
u
n
t
∗
v
a
l
u
e
/
2
)
d
p
[
i
]
[
1
]
=
d
p
[
f
a
]
[
0
]
+
c
o
u
n
t
∗
v
a
l
u
e
dp[i][0] = min(dp[fa][0], dp[fa][1]) + (count*value/2) \\ dp[i][1] = dp[fa][0] + count*value
dp[i][0]=min(dp[fa][0],dp[fa][1])+(count∗value/2)dp[i][1]=dp[fa][0]+count∗value
实现细节可以看代码。
class Solution {
public:
int minimumTotalPrice(int n, vector<vector<int>>& edges, vector<int>& price, vector<vector<int>>& trips) {
vector<vector<int>> map(n);
vector<int> count(n, 0);
for(auto &e: edges){
map[e[0]].push_back(e[1]);
map[e[1]].push_back(e[0]);
}
//DFS累计每个点的访问次数
function<bool(int,int,int)> DFS = [&](int x, int fa, int tar) -> bool{
if(x == tar){
count[x]++;
return true;
}
for(int y: map[x]){
if(y == fa) continue;
if(DFS(y, x, tar)){
count[x]++;
return true;
}
}
return false;
};
for(auto &t: trips) DFS(t[0], -1, t[1]);
//树上DP用于统计答案
function<pair<int,int>(int, int)>dp = [&](int x,int fa) -> pair<int,int>{
pair<int, int> ans = {price[x]*count[x], price[x]*count[x]/2};
for(int y: map[x]){
if(y == fa) continue;
auto [val0, val1] = dp(y, x);
ans.first += min(val0, val1);
ans.second += val0; // dp更新可能最小值
}
return ans;
};
auto [ans0, ans1] = dp(0, -1);
return min(ans0, ans1);
}
};
12/07 1466. 重新规划路线
给定一张单向图,重新定向所有路线,使得所有节点都能到达节点0,返回需要变更方向的最小路线数。
题目链接
反向思考,我们考虑从节点0到达所有节点,路上所经过的所有反向边(即从0指向子节点的边)就是需要变更方向的。
由于是反向遍历,因此遍历过程中发现某条边方向是由当前节点指向邻节点的,该边就是一个反向边。
在建图时,我们将所有单向边建为双向边,并引入方向指示位(0代表正向边,1代表反向边)
DFS统计时直接数1就好。
class Solution {
public:
int minReorder(int n, vector<vector<int>>& connections) {
vector<vector<pair<int, int>>> map(n);
for(auto &e: connections){
map[e[0]].push_back({e[1], 1});//由当前节点指向邻节点即是反向边
map[e[1]].push_back({e[0], 0});
}
function<int(int, int)> DFS = [&](int x, int fa)->int{
int ans = 0;
for(auto &y: map[x]){
if(y.first == fa) continue;
ans += y.second + DFS(y.first,x);
}
return ans;
};
return DFS(0, -1);
}
};
12/08 2008. 出租车的最大盈利
你沿着一条路单向开车,路上有一系列的订单,以【起点,终点,小费】的格式给出,每单你能盈利终点-起点+小费的金额;
同一时刻你最多能接一单订单,返回最大盈利值。
经典dp问题,dp[i]代表车开到i位置时的最大盈利值,转移方程为:
d
p
[
i
]
=
m
a
x
(
d
p
[
i
−
1
]
,
d
p
[
s
t
a
r
t
[
x
]
]
+
e
n
d
[
x
]
−
s
t
a
r
t
[
x
]
+
t
i
p
[
x
]
)
,
e
n
d
[
x
]
≤
i
dp[i] = max(dp[i-1], dp[start[x]] + end[x] - start[x] + tip[x]), end[x] \le i
dp[i]=max(dp[i−1],dp[start[x]]+end[x]−start[x]+tip[x]),end[x]≤i
在处理时,根据结束点存储订单,方便dp。
class Solution {
public:
long long maxTaxiEarnings(int n, vector<vector<int>>& rides) {
vector<long long>dp(n+1, 0);
vector<vector<pair<int,int>>> end(n+1);
for(auto &r: rides) end[r[1]].push_back({r[0], r[2]});//结束点上存储订单
for(int i=1;i<=n;i++){
dp[i] = dp[i-1];
for(auto x:end[i]) dp[i] = max(dp[i], dp[x.first]+i-x.first+x.second);
}
return dp[n];
}
};
12/09 2048. 下一个更大的数值平衡数
如果整数 x 满足:对于每个数位 d ,这个数位 恰好 在 x 中出现 d 次。那么整数 x 就是一个 数值平衡数 。
给你一个整数 n ,请你返回 严格大于 n 的 最小数值平衡数 。
题目链接
OEIS欢迎你(笑)
实际是一道打表题,没有难度,没啥好说的,连代码都不想贴。
只需要知道1224444是大于
1
0
6
10^6
106的第一个数值平衡树,然后枚举就完事儿了。
我把表给出来。
int beauty[110]={1,
22,
122,212,221,333,
1333,3133,3313,3331,4444,
14444,22333,23233,23323,23332,32233,32323,32332,
33223,33232,33322,41444,44144,44414,44441,55555,
122333,123233,123323,123332,132233,132323,132332,
133223,133232,133322,155555,212333,213233,213323,
213332,221333,223133,223313,223331,224444,231233,
231323,231332,232133,232313,232331,233123,233132,
233213,233231,233312,233321,242444,244244,244424,
244442,312233,312323,312332,313223,313232,313322,
321233,321323,321332,322133,322313,322331,323123,
323132,323213,323231,323312,323321,331223,331232,
331322,332123,332132,332213,332231,332312,332321,
333122,333212,333221,422444,424244,424424,424442,
442244,442424,442442,444224,444242,444422,515555,
551555,555155,555515,555551,666666,
1224444};
12/10 70. 爬楼梯
这周真是虎头蛇尾(
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
题目链接
同样没有贴代码的必要。
d
p
[
i
]
=
d
p
[
i
−
1
]
+
d
p
[
i
−
2
]
dp[i] = dp[i-1] + dp[i-2]
dp[i]=dp[i−1]+dp[i−2]