力扣秋季编程大赛-赛后题解

2023大厂真题提交网址(含题解):

www.CodeFun2000.com(http://101.43.147.120/)

最近我们一直在将收集到的机试真题制作数据并搬运到自己的OJ上,供大家免费练习,体会真题难度。现在OJ已录入50+道2023年最新大厂真题,同时在不断的更新。同时,可以关注"塔子哥学算法"公众号获得每道题的题解。
在这里插入图片描述

前言:

CSDN的第一篇博客.然后比赛的时候发挥的不是很好,前15分钟切完三题就被D题关住了。赛后补题发现其实D,E都算不上什么难题。没办法,只能是自己知识面太窄了.多积累吧

题单

1.速算机器人
2.早餐组合
3.秋叶收藏集
4.快速公交
5.追逐游戏

1.速算机器人

语法题

2.早餐组合

题意:
给你两个数组a,b以及一个数T。让你从a中选出一个数X,b中选出一个数Y,使得 X + Y ≤ T X + Y \leq T X+YT. 让你求方案数. ( a , b 数组长度 ≤ 1 e 5 ) (a,b数组长度\leq1e5) (a,b数组长度1e5)
思路:
对a,b排序后枚举a数组的X. 由等式得到 Y ≤ T − X Y \leq T -X YTX. 二分b数组计数即可.
复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

#define ll long long 
class Solution {
public:
    int breakfastNumber(vector<int>& a, vector<int>& b, int x) {
        sort(a.begin() , a.end());
        sort(b.begin() , b.end());
        ll mod = 1e9 + 7 , ans = 0;
        for (auto g : a){
            if (g >= x) break;
            ll pos = upper_bound(b.begin(),b.end() , x - g) - b.begin();
            ans = (ans + pos) % mod;
        }
        return ans;
    }
};

3.秋叶收藏集

题意:
给你只含R,B的字符串S。你一次可以将R修改成B,也可以将B修改成R。问最小修改代价使得字符串S形如: { R . . . R B . . . B R . . . R } . \{R...RB...BR...R\}. {R...RB...BR...R}.
即将字符串分成三段,第一三段全为R,第二段全为B。注意到每一段长度至少为1. ( ∣ S ∣ ≤ 1 e 5 ) (|S| \leq 1e5) (S1e5)
思路:
经典动态规划题 . 有一个技巧在于将状态分解成三段。 经典动态规划题.有一个技巧在于将状态分解成三段。 经典动态规划题.有一个技巧在于将状态分解成三段。
即状态1为: 字符串形如:    { R . . . R } \ \ \{R...R\}   {R...R}
状态2为: 字符串形如:      { R . . . R B . . . B } \ \ \ \ \{R...RB...B\}     {R...RB...B}
状态3为: 字符串形如:       { R . . . R B . . . B R . . . R } \ \ \ \ \ \{R...RB...BR...R\}      {R...RB...BR...R}
我们可以发现,这三个状态有很强的相关性。即状态2一定是由状态1得到.状态3一定是由1 得到 2 再 得到 3.
所以令 d p ( i , 0 / 1 / 2 ) dp(i,0/1/2) dp(i,0/1/2)代表前i个字符中,将i变成状态 0 / 1 / 2 0/1/2 0/1/2的最小代价.考虑 d p ( i ) dp(i) dp(i) d p ( i − 1 ) dp(i - 1) dp(i1)之间的3个状态的转移:
d p ( i , 0 ) = d p ( i − 1 , 0 ) + S [ i ] = = B dp(i,0) = dp(i-1,0)+S[i]==B dp(i,0)=dp(i1,0)+S[i]==B
d p ( i , 1 ) = m i n ( d p ( i − 1 , 0 ) , d p ( i − 1 , 1 ) ) + S [ i ] = = R dp(i,1) = min(dp(i-1,0),dp(i-1,1))+S[i]==R dp(i,1)=min(dp(i1,0),dp(i1,1))+S[i]==R
d p ( i , 2 ) = m i n ( d p ( i − 1 , 1 ) , d p ( i − 1 , 2 ) ) + S [ i ] = = B dp(i,2) = min(dp(i-1,1),dp(i-1,2))+S[i]==B dp(i,2)=min(dp(i1,1),dp(i1,2))+S[i]==B

class Solution {
public:
    int dp[100005][5];
    int minimumOperations(string a) {
        int n = a.size();
        dp[1][1] = (a[0] == 'y');
        dp[1][2] = 1e8;
        dp[1][3] = 1e8;
        for (int i = 2 ; i <= n ; i++){
            char g = a[i - 1];
            dp[i][1] = dp[i - 1][1] + (g == 'y');
            dp[i][2] = min (dp[i - 1][1] , dp[i - 1][2]) + (g == 'r');
            dp[i][3] = min (dp[i - 1][2] , dp[i - 1][3]) + (g == 'y');
        }
        return dp[n][3];
    }
};

4.快速公交

题意:
给你一个数轴,你最开始站在0点.在每一个点X你可以选择向前走,向后走,或者搭乘公交i: X − > j u m p [ i ] × X X -> jump[i] \times X X>jump[i]×X.每一种操作都有他们自己的花费。求从0到T点的最小花费.( 公交个数 ≤ 10 公交个数\leq10 公交个数10, 坐标 T ≤ 1 e 9 坐标T\leq1e9 坐标T1e9).
题目思路:
这道题的处理难点在于T过于庞大。如果直接跑Dijstra或会导致超时,因为本质上他是在搜索空间里做dp,那关键点在于如何减少不必要的搜索空间
①考虑一个子问题:若没有+1,-1的操作,只考虑坐公交的操作(保证有解)
正向思考比较困难,考虑到一定有解。那么不妨自顶向下的考虑。从T开始,若 T % j u m p [ i ] = = 0 T\% jump[i]==0 T%jump[i]==0.那么递归到 T j u m p [ i ] \frac{T}{jump[i]} jump[i]T点尝试求解(显然,自顶向下时我们是再考虑上一步从哪里来的,那些不能整除T的公交一定不可能是上一步).再加上记忆化即可
而且由于是做除法,那么最多   l o g min ⁡ { j u m p [ i ] } T \ log_{\min{\{jump[i]\}}}T  logmin{jump[i]}T 步能够到达1点.

②再来考虑有+1,-1的情况:
还是自顶向下的考虑,但这两种决策我们一定不能把他考虑进dp的决策里,不然搜索空间就爆炸了.
这时我们发现一个点:有了+1,-1操作之后,那些 T % j u m p [ i ] ≠ 0 T\%jump[i]\neq0 T%jump[i]=0的点也能放入决策中了!因为我们可以通过+1/-1来移动到jump[i]的整除点!这样以来+1/-1的作用仅仅就是将当前位置转移到 每个jump[i]的整除点了!到这里问题基本就解决了.

#define ll long long
class Solution {
public:
    unordered_map<ll,ll>dp;
    ll costl ,  costr;
    ll dfs (ll x , vector<int>& a, vector<int>& b)
    {
        if (x == 1) return costr;
        if (dp.find(x) != dp.end()) return dp[x];
        ll ans = dfs (1 , a , b) + (x - 1) * costr;
        for (int i = 0 ; i < a.size() ; i++){
            if (x >= a[i]) ans = min (ans , dfs(x / a[i] , a , b) + b[i] + (x % a[i]) * costr );
            ans = min (ans , dfs((x - 1) / a[i] + 1, a , b) + b[i] + (a[i] - (x % a[i])) * costl);
        }
        return dp[x] = ans;
    } 
    int busRapidTransit(int s, int r, int l, vector<int>& a, vector<int>& b) {
        costl = l;
        costr = r;
        int mod = 1e9 + 7;
        return dfs(s , a , b) % mod;
    }
};

5.追逐游戏

题意:
给你一颗基环树,两个人在树上博弈,一个人追另一个人。设追逐者为A,被追逐者为B。A先动,问最少多少回合A能够追到B。或者输出-1代表永远无法追到.
思路:
一道挺思维的题.由于是基环树,所以只有一个环,那么在纸上画画图找规律不难想到

当开始时两者相邻,则1回合结束战斗
当环的大小=3时,必能被追到.
当环的大小 ≥ 4 \geq 4 4时,若开始B在环上或者B能够在A抓到B之间到达环上,就永远追不到.反之必能被追到.

所以在B必能被抓到的情况下,B优先跑到离A最远的点i,且点i满足 d i s t B [ i ] + 1 < d i s t A [ i ] dist_B[i]+1distB[i]+1<distA[i]

过程涉及到找环以及求单源最短路,考虑使用dfs和bfs求解。还有一些细节可以看看代码.

const int maxn = 100005;
const int inf = 1e9;
class Solution {
public:
    vector<int> E[maxn];
    int book[maxn] , dep[maxn] ,iscir[maxn] , dist_s[maxn] , dist_e[maxn] , n;
    int cir_len;
    stack<int> vis;
    void findcir(int u , int fa , int step)
    {
        book[u] = 1;
        dep[u] = step;
        vis.push(u);
        for (auto v : E[u]){
            if (v == fa) continue;
            if (book[v] == 2) continue;
            // 遇到环
            if (book[v] == 1){
                cir_len = step - dep[v] + 1;
                // 标记环上节点
                iscir[v] = true;
                stack<int> temp;
                while (vis.top() != v) {
                    int g = vis.top();vis.pop();
                    iscir[g] = true;
                    temp.push(g);
                }
                while (temp.size()){
                    int g = temp.top();temp.pop();
                    vis.push(g);
                }
                continue;
            }
            findcir (v , u , step + 1);
        }
        book[u] = 2;
        vis.pop();
    }
    void bfs (int s , int dist[])
    {
        for (int i = 1 ; i <= n ; i++) dist[i] = inf;
        dist[s] = 0;
        queue<int> q;
        q.push(s);
        while (q.size()){
            int u = q.front(); q.pop();
            for (auto v : E[u])
                if (dist[v] == inf || dist[v] > dist[u] + 1)
                    dist[v] = dist[u] + 1 , q.push(v);
        }
    }
    int chaseGame(vector<vector<int>>& edges, int s, int e) {
        n = edges.size();
        for (auto g : edges){
            E[g[0]].push_back(g[1]);
            E[g[1]].push_back(g[0]);
        }
        for (auto v : E[s]) if (v == e) return 1;
        findcir(1 , 0 , 0);
        bfs (s , dist_s);
        bfs (e , dist_e);
        if (cir_len == 3){
            // 在三元环内部会让距离减少1
            int gap = 0;
            if (iscir[e]) {
                bool ok = true;
                for (auto v : E[e]){
                    if (iscir[v] == false) {
                        ok = false;
                        break;
                    }
                }
                gap = ok;
            }
            if (dist_s[e] - gap == 1) return 2;
            // 现在找一个点使得 dist_s > dist_e + 1 的 离 dist_s 最远的点
            int maxx = 0;
            for (int i = 1 ; i <= n ; i++){
                if (dist_s[i] > dist_e[i] + 1){
                    if (maxx < dist_s[i]){
                        maxx = dist_s[i];
                    }
                }
            }
            return maxx;
        }
        // 初始化在环上,则永远无法遇到
    //    cout << iscir[e] << endl;
     //   for (auto v : E[50]) cout << v << " ";
  //      cout << endl;
        if (iscir[e]) return -1;
        // 不在环上,那看小扣能不能比小力先到环上任意一个点
        for (int i = 1 ; i <= n ; i++){
            if (iscir[i] == false) continue;
            if (dist_s[i] > dist_e[i] + 1) {
  //              cout << "g " << endl;
                return -1;
            }
        }
        // 不行的话就找一个点使得 dist_s > dist_e + 1 的 离 dist_s 最远的点
        int maxx = 0;
        for (int i = 1 ; i <= n ; i++){
            if (dist_s[i] > dist_e[i] + 1){
                if (maxx < dist_s[i]){
                    maxx = dist_s[i];
                }
            }
        }
        return maxx;
    }
};

你可能感兴趣的:(算法,动态规划,图论)