算法竞赛>力扣>双周赛 | biweekly-contest-160

3602. 十六进制和三十六进制转化

解题思路

转换、拼接。

代码实现

string concatHex36(int n) {
    // 数字转字符
    auto c = [&](int x) {
        if (x < 10)return '0' + x;
        return 'A' + (x - 10);
    };
    // 将x转为p进制
    auto sv = [&](int x, int p) {
        string s;
        while (x) s.push_back(c(x % p)), x /= p;
        reverse(s.begin(), s.end());
        return s;
    };
    // 拼接
    return sv(n * n, 16) + sv(n * n * n, 36);
}

时间复杂度 \(O(\log n)\),空间复杂度 \(O(\log n)\)

3603. 交替方向的最小路径代价 II

解题思路

进入该单元格需要付出成本 \((i + 1) * (j + 1)\)

只有奇数秒才能进入下一个单元格,当进入到单元格 \((i, j)\) 后,必为偶数秒,必须等待,等到下一秒才能继续前进。

综上,对于单元格 \((i, j)\),需要付出的成本为 \((i + 1) * (j + 1) + waitCost[i][j]\)

特别的,对于起点和终点,无需等待。

代码实现

反向动态记忆化搜索

后面的由前面的推出。

long long minCost(int m, int n, vector<vector<int>>& waitCost) {
    typedef long long ll;
    vector dp(m, vector<ll>(n, -1));
    function<ll(int, int)> dfs = [&](int i, int j) -> long long {
        if (i < 0 || j < 0) return LLONG_MAX;
        // 起点只有进入成本
        if (i == 0 && j == 0) return 1;
        ll& v = dp[i][j];
        if (v != -1) return v;
        // 当前单元格的成本+前序单元格的成本
        return v = min(dfs(i, j - 1), dfs(i - 1, j)) + waitCost[i][j] + (i + 1) * (j + 1);
    };
    // 终点只有进入成本
    return dfs(m - 1, n - 1) - waitCost[m - 1][n - 1];
}

时间复杂度 \(O(mn)\),空间复杂度 \(O(mn)\)

正向记忆化搜索

前面的由后面的推出。

long long minCost_2(int m, int n, vector<vector<int>>& waitCost) {
    typedef long long ll;
    vector dp(m, vector<ll>(n, -1));
    function<ll(int, int)> dfs = [&](int i, int j) {
        if (i >= m || j >= n)return LLONG_MAX;
        ll& v = dp[i][j];
        if (v != -1)return v;
        v = (i + 1) * (j + 1) + waitCost[i][j];
        // 终点
        if (i == m - 1 && j == n - 1)return v;
        return v += min(dfs(i + 1, j), dfs(i, j + 1));
    };
    // 起点和终点无需等待
    return dfs(0, 0) - waitCost[0][0] - waitCost[m - 1][n - 1];
}

时间复杂度 \(O(mn)\),空间复杂度 \(O(mn)\)

递推

由反向动态记忆化搜索翻译得到。

long long minCost_3(int m, int n, vector<vector<int>>& waitCost) {
    typedef long long ll;
    vector dp(m + 1, vector<ll>(n + 1, LLONG_MAX));
    // 边界值
    dp[0][1] = dp[1][0] = -waitCost[0][0];
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++)
            // 从上面来或者从左边来的成本+当前方格的成本
            dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + i * j + waitCost[i - 1][j - 1];
    return dp[m][n] - waitCost[m - 1][n - 1];
}

时间复杂度 \(O(mn)\),空间复杂度 \(O(mn)\)

3604. 有向图中到达终点的最少时间

解题思路

节点的到达时间由前序节点的到达时间决定,只需要从前往后遍历节点即可。

具体该按何种顺序遍历节点呢?每次可拓展到达时间最早的节点,即 Djstra 算法。

代码实现

int minTime(int n, vector<vector<int>>& edges) {
    vector<vector<tuple<int, int, int>>> es(n);
    vector<int> d(n,INT_MAX);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> q;
    d[0] = 0;
    // 邻接矩阵
    for (auto& v : edges)
        es[v[0]].emplace_back(v[1], v[2], v[3]);
    q.emplace(0, 0);
    while (!q.empty()) {
        const auto [dx,x] = q.top();
        q.pop();
        // 当前用时大于最短用时
        if (dx > d[x])continue;
        // 终点
        if (x == n - 1) return dx;
        // 前进
        for (auto& [y, s, e] : es[x]) {
            // 超时导致边不可用
            if (d[x] > e)continue;
            // 当前最早完成时间
            int t = max(d[x], s) + 1;
            // 全局最早完成时间
            if (d[y] > t) d[y] = t, q.emplace(t, y);
        }
    }
    return -1;
}

时间复杂度 \(O(n+m\log m)\),空间复杂度 \(O(n + m)\)

原文链接

算法竞赛>力扣>双周赛 | biweekly-contest-160

posted @ 2025-07-08 09:48  字节幺零二四  阅读(25)  评论(0)    收藏  举报