Leetcode Test
2477 到达首都的最少油耗(12.5)
给你一棵 n
个节点的树(一个无向、连通、无环图),每个节点表示一个城市,编号从 0
到 n - 1
,且恰好有 n - 1
条路。0
是首都。给你一个二维整数数组 roads
,其中 roads[i] = [ai, bi]
,表示城市 ai
和 bi
之间有一条 双向路 。
每个城市里有一个代表,他们都要去首都参加一个会议。
每座城市里有一辆车。给你一个整数 seats
表示每辆车里面座位的数目。
城市里的代表可以选择乘坐所在城市的车,或者乘坐其他城市的车。相邻城市之间一辆车的油耗是一升汽油。
请你返回到达首都最少需要多少升汽油。
提示:
1 <= n <= 105
roads.length == n - 1
roads[i].length == 2
0 <= ai, bi < n
ai != bi
roads
表示一棵合法的树。1 <= seats <= 105
【贪心 + dfs】
class Solution {
public:
long long minimumFuelCost(vector<vector<int>>& roads, int seats) {
long long ans = 0;
// 老规矩:看到图构建邻接矩阵
vector<vector<int>> citys(roads.size() + 1); // 这里使用roads.size()+1的原因是城市数可能比道路多一个
for (const auto& road: roads) {
citys[road[0]].push_back(road[1]);
citys[road[1]].push_back(road[0]);
}
// lambda表达式构造DFS深搜
function<long long(int, int)> dfs = [&](int x, int father) -> int {
long long size = 1; // size是统计此结点下的子树大小
for (const int& y: citys[x]) {
if (y != father) size += dfs(y, x); // 统计子树大小
}
// 如果x不是根结点
if (x != 0) {
// !!!最难理解的部分
// 这里的ans加的是子树到达此处结点需要消耗的燃料量
// 可以理解为前缀和,因为seats的存在所以每到一个结点都要计算一遍
ans += (size + seats - 1) / seats; // 经典向上取整算法(x+m-1)/m
}
return size;
};
// 这段代码的起点——启动DFS
dfs(0, -1); // 0是根结点,-1代表0没有父结点
return ans;
}
};
2646 最小化旅行的价格总和(12.6)
现有一棵无向、无根的树,树中有 n
个节点,按从 0
到 n - 1
编号。给你一个整数 n
和一个长度为 n - 1
的二维整数数组 edges
,其中 edges[i] = [ai, bi]
表示树中节点 ai
和 bi
之间存在一条边。
每个节点都关联一个价格。给你一个整数数组 price
,其中 price[i]
是第 i
个节点的价格。
给定路径的 价格总和 是该路径上所有节点的价格之和。
另给你一个二维整数数组 trips
,其中 trips[i] = [starti, endi]
表示您从节点 starti
开始第 i
次旅行,并通过任何你喜欢的路径前往节点 endi
。
在执行第一次旅行之前,你可以选择一些 非相邻节点 并将价格减半。
返回执行所有旅行的最小价格总和。
提示:
1 <= n <= 50
edges.length == n - 1
0 <= ai, bi <= n - 1
edges
表示一棵有效的树price.length == n
price[i]
是一个偶数1 <= price[i] <= 1000
1 <= trips.length <= 100
0 <= starti, endi <= n - 1
【dfs】
class Solution {
public:
int minimumTotalPrice(int n, vector<vector<int>> &edges, vector<int> &price, vector<vector<int>> &trips) {
vector<vector<int>> g(n);
for (auto &e: edges) {
int x = e[0], y = e[1];
g[x].push_back(y);
g[y].push_back(x); // 建树
}
vector<int> cnt(n);
for (auto &t: trips) {
int end = t[1];
function<bool(int, int)> dfs = [&](int x, int fa) -> bool {
if (x == end) {
cnt[x]++;
return true; // 找到 end
}
for (int y: g[x]) {
if (y != fa && dfs(y, x)) {
cnt[x]++; // x 是 end 的祖先节点,也就在路径上
return true;
}
}
return false; // 未找到 end
};
dfs(t[0], -1);
}
// 类似 337. 打家劫舍 III
function<pair<int, int>(int, int)> dfs = [&](int x, int fa) -> pair<int, int> {
int not_halve = price[x] * cnt[x]; // x 不变
int halve = not_halve / 2; // x 减半
for (int y: g[x]) {
if (y != fa) {
auto [nh, h] = dfs(y, x); // 计算 y 不变/减半的最小价值总和
not_halve += min(nh, h); // x 不变,那么 y 可以不变,可以减半,取这两种情况的最小值
halve += nh; // x 减半,那么 y 只能不变
}
}
return {not_halve, halve};
};
auto [nh, h] = dfs(0, -1);
return min(nh, h);
}
};
1466 重新规划路线(12.7)
n
座城市,从 0
到 n-1
编号,其间共有 n-1
条路线。因此,要想在两座不同城市之间旅行只有唯一一条路线可供选择(路线网形成一颗树)。去年,交通运输部决定重新规划路线,以改变交通拥堵的状况。
路线用 connections
表示,其中 connections[i] = [a, b]
表示从城市 a
到 b
的一条有向路线。
今年,城市 0 将会举办一场大型比赛,很多游客都想前往城市 0 。
请你帮助重新规划路线方向,使每个城市都可以访问城市 0 。返回需要变更方向的最小路线数。
题目数据 保证 每个城市在重新规划路线方向后都能到达城市 0 。
提示:
2 <= n <= 5 * 10^4
connections.length == n-1
connections[i].length == 2
0 <= connections[i][0], connections[i][1] <= n-1
connections[i][0] != connections[i][1]
【dfs】
class Solution {
public:
int dfs(int x, int parent, vector<vector<pair<int, int>>>& e) {
int res = 0;
for (auto &edge : e[x]) {
if (edge.first == parent) {
continue;
}
res += edge.second + dfs(edge.first, x, e);
}
return res;
}
int minReorder(int n, vector<vector<int>>& connections) {
vector<vector<pair<int, int>>> e(n);
for (auto edge : connections) {
e[edge[0]].push_back(make_pair(edge[1], 1));
e[edge[1]].push_back(make_pair(edge[0], 0));
}
return dfs(0, -1, e);
}
};
2008 出租车的最大盈利(12.8)
你驾驶出租车行驶在一条有 n
个地点的路上。这 n
个地点从近到远编号为 1
到 n
,你想要从 1
开到 n
,通过接乘客订单盈利。你只能沿着编号递增的方向前进,不能改变方向。
乘客信息用一个下标从 0 开始的二维数组 rides
表示,其中 rides[i] = [starti, endi, tipi]
表示第 i
位乘客需要从地点 starti
前往 endi
,愿意支付 tipi
元的小费。
每一位 你选择接单的乘客 i
,你可以 盈利 endi - starti + tipi
元。你同时 最多 只能接一个订单。
给你 n
和 rides
,请你返回在最优接单方案下,你能盈利 最多 多少元。
**注意:**你可以在一个地点放下一位乘客,并在同一个地点接上另一位乘客。
提示:
1 <= n <= 105
1 <= rides.length <= 3 * 104
rides[i].length == 3
1 <= starti < endi <= n
1 <= tipi <= 105
【dp递推】
class Solution {
public:
long long maxTaxiEarnings(int n, vector<vector<int>> &rides) {
vector<vector<pair<int, int>>> groups(n + 1);
for (auto &r : rides) {
int start = r[0], end = r[1], tip = r[2];
groups[end].push_back(make_pair(start, end - start + tip));
}
vector<long long> f(n + 1);
for (int i = 2; i <= n; i++) {
f[i] = f[i - 1];
for (auto &[s, t] : groups[i]) {
f[i] = max(f[i], f[s] + t);
}
}
return f[n];
}
};
2048 下一个更大的数值平衡数(12.9)
如果整数 x
满足:对于每个数位 d
,这个数位 恰好 在 x
中出现 d
次。那么整数 x
就是一个 数值平衡数 。
给你一个整数 n
,请你返回 严格大于 n
的 最小数值平衡数 。
提示:
0 <= n <= 106
【枚举】
int isBalance(int x) {
int count[10] = {0};
while (x > 0) {
count[x % 10]++;
x /= 10;
}
for (int i = 0; i < 10; i++) {
if (count[i] > 0 && count[i] != i) {
return 0;
}
}
return 1;
}
int nextBeautifulNumber(int n) {
for (int i = n + 1; i <= 1224444; i++) {
if (isBalance(i)) {
return i;
}
}
return -1;
}
70 爬楼梯(12.10)
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
提示:
1 <= n <= 45
【dp】
int climbStairs(int n) {
//f[i] = f[i-1] + f[i-2]
/*
//会超时间
if(n<=1){
return 1;
}
else{
return climbStairs(n-1)+climbStairs(n-2);
}
*/
int *f=malloc(sizeof(int)*(n+1));
f[0]=1;
f[1]=1;
for(int i=2;i<=n;i++){
f[i]=f[i-1]+f[i-2];
}
return f[n];
}
1631 最小体力消耗路径(12.11)
你准备参加一场远足活动。给你一个二维 rows x columns
的地图 heights
,其中 heights[row][col]
表示格子 (row, col)
的高度。一开始你在最左上角的格子 (0, 0)
,且你希望去最右下角的格子 (rows-1, columns-1)
(注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动,你想要找到耗费 体力 最小的一条路径。
一条路径耗费的 体力值 是路径上相邻格子之间 高度差绝对值 的 最大值 决定的。
请你返回从左上角走到右下角的最小 体力消耗值 。
提示:
rows == heights.length
columns == heights[i].length
1 <= rows, columns <= 100
1 <= heights[i][j] <= 106
【二分法】(一开始还以为是dp,偷懒cv了。)
int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int minimumEffortPath(int** heights, int heightsSize, int* heightsColSize) {
int m = heightsSize;
int n = heightsColSize[0];
int left = 0, right = 999999, ans = 0;
while (left <= right) {
int mid = (left + right) / 2;
int q[n * m][2];
int qleft = 0, qright = 0;
q[qright][0] = 0, q[qright++][1] = 0;
int seen[m * n];
memset(seen, 0, sizeof(seen));
seen[0] = 1;
while (qleft < qright) {
int x = q[qleft][0], y = q[qleft++][1];
for (int i = 0; i < 4; ++i) {
int nx = x + dirs[i][0];
int ny = y + dirs[i][1];
if (nx >= 0 && nx < m && ny >= 0 && ny < n && !seen[nx * n + ny] && abs(heights[x][y] - heights[nx][ny]) <= mid) {
q[qright][0] = nx, q[qright++][1] = ny;
seen[nx * n + ny] = 1;
}
}
}
if (seen[m * n - 1]) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;
}