【Leetcode Sheet】Weekly Practice 19

Leetcode Test

2477 到达首都的最少油耗(12.5)

给你一棵 n 个节点的树(一个无向、连通、无环图),每个节点表示一个城市,编号从 0n - 1 ,且恰好有 n - 1 条路。0 是首都。给你一个二维整数数组 roads ,其中 roads[i] = [ai, bi] ,表示城市 aibi 之间有一条 双向路

每个城市里有一个代表,他们都要去首都参加一个会议。

每座城市里有一辆车。给你一个整数 seats 表示每辆车里面座位的数目。

城市里的代表可以选择乘坐所在城市的车,或者乘坐其他城市的车。相邻城市之间一辆车的油耗是一升汽油。

请你返回到达首都最少需要多少升汽油。

提示:

  • 1 <= n <= 105
  • roads.length == n - 1
  • roads[i].length == 2
  • 0 <= ai, bi < n
  • ai != bi
  • roads 表示一棵合法的树。
  • 1 <= seats <= 105

【贪心 + dfs】

cpp 复制代码
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 个节点,按从 0n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 aibi 之间存在一条边。

每个节点都关联一个价格。给你一个整数数组 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】

cpp 复制代码
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 座城市,从 0n-1 编号,其间共有 n-1 条路线。因此,要想在两座不同城市之间旅行只有唯一一条路线可供选择(路线网形成一颗树)。去年,交通运输部决定重新规划路线,以改变交通拥堵的状况。

路线用 connections 表示,其中 connections[i] = [a, b] 表示从城市 ab 的一条有向路线。

今年,城市 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】

cpp 复制代码
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 个地点从近到远编号为 1n ,你想要从 1 开到 n ,通过接乘客订单盈利。你只能沿着编号递增的方向前进,不能改变方向。

乘客信息用一个下标从 0 开始的二维数组 rides 表示,其中 rides[i] = [starti, endi, tipi] 表示第 i 位乘客需要从地点 starti 前往 endi ,愿意支付 tipi 元的小费。

每一位 你选择接单的乘客 i ,你可以 盈利 endi - starti + tipi 元。你同时 最多 只能接一个订单。

给你 nrides ,请你返回在最优接单方案下,你能盈利 最多 多少元。

**注意:**你可以在一个地点放下一位乘客,并在同一个地点接上另一位乘客。

提示:

  • 1 <= n <= 105
  • 1 <= rides.length <= 3 * 104
  • rides[i].length == 3
  • 1 <= starti < endi <= n
  • 1 <= tipi <= 105

【dp递推】

cpp 复制代码
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

【枚举】

c 复制代码
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 阶你才能到达楼顶。

每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

提示:

  • 1 <= n <= 45

【dp】

c 复制代码
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了。)

c 复制代码
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;
}
相关推荐
passer__jw7671 小时前
【LeetCode】【算法】3. 无重复字符的最长子串
算法·leetcode
passer__jw7671 小时前
【LeetCode】【算法】21. 合并两个有序链表
算法·leetcode·链表
sweetheart7-71 小时前
LeetCode22. 括号生成(2024冬季每日一题 2)
算法·深度优先·力扣·dfs·左右括号匹配
__AtYou__3 小时前
Golang | Leetcode Golang题解之第557题反转字符串中的单词III
leetcode·golang·题解
2401_858286113 小时前
L7.【LeetCode笔记】相交链表
笔记·leetcode·链表
景鹤4 小时前
【算法】递归+回溯+剪枝:78.子集
算法·机器学习·剪枝
_OLi_4 小时前
力扣 LeetCode 704. 二分查找(Day1:数组)
算法·leetcode·职场和发展
丶Darling.4 小时前
Day40 | 动态规划 :完全背包应用 组合总和IV(类比爬楼梯)
c++·算法·动态规划·记忆化搜索·回溯
风影小子5 小时前
IO作业5
算法
奶味少女酱~5 小时前
常用的c++特性-->day02
开发语言·c++·算法