Leetcode双周赛117总结

文章目录

      • Q1 给小朋友们分糖果I
      • Q2 给小朋友们分糖果II
      • Q3 重新排列后包含指定子字符串的字符串数目
      • Q4 购买物品的最大开销

Q1 给小朋友们分糖果I

  • 题目链接

  • 解题思路:

    • 来一点暴力震撼
    • 三重循环枚举三个小朋友可能取的糖果[0, limit]
    • 如果三个小朋友取到的糖果总数刚好是n,答案加1
  • 解题代码:

class Solution {
public:
    int distributeCandies(int n, int limit) {
        int ans = 0;
        for(int i = 0; i <= limit; i++)
        {
            for(int j = 0; j <= limit; j++)
            {
                for(int k = 0; k <= limit; k++)
                {
                    if(i + j + k == n)
                        ans += 1;
                }
            }
        }
        return ans;
    }
};

  • On^2的做法:

class Solution {
public:
    int distributeCandies(int n, int limit) {
        int ans = 0;
        for(int i = 0; i <= limit; i++)
        {
            for(int j = 0; j <= limit; j++)
            {
                int k = n - i - j;
                if(k >= 0 && k <= limit)
                    ans += 1;
            }
        }
        return ans;
    }
};

Q2 给小朋友们分糖果II

  • 题目链接

  • 解题思路1:

    • 设三个小朋友分到的糖果数分别为a、b、c
    • 枚举第一个小朋友分到的糖果数a,问题变成 b + c = n - a,b和c有多少组解,只要判断b的取值范围就可以
    • b本身满足 0 <= b <= limit
    • 又因为c也要满足0 <= c <= limit,因此 0 <= n-a-b <= limit,解得 n-limit-a <= b <= n-a,因此 max(0, n-limit-a) <= b <= min(limit, n-a),b可以取的值个数就是max(0, min(limit, n-a) - max(0, n-limit-a) + 1)
  • 解题代码:


class Solution {
public:
    long long distributeCandies(int n, int limit) {
        long long ans = 0;
        for(int a = 0; a <= limit; a++)
            ans += max(0, min(n-a, limit) - max(0, n-limit-a) + 1);
        return ans;
    }
};

  • 解题思路2:

    • 容斥原理
    • 答案 = 全部的方案数 - 3 * 有一个小朋友大于limit的方案数(A、B、C) + 3 * 有两个小朋友大于limit的方案数(AB、AC、BC) - 有三个小朋友大于limit的方案数
    • 方案数怎么求:
      • 全部的方案数,相当于把n个小球和2个挡板放到n+2的位置上,2个挡板共有C(n+2, 2)种方案
      • 有一个小朋友大于limit的方案 相当于把n - (limit + 1)和 2个挡板进行放置,2个挡板有C(n - (limit+1) + 2, 2)种方案
      • 有两个小朋友大于limit的方案 相当于把n - 2 * (limit + 1)和 2个挡板进行放置,2个挡板有C(n - 2 * (limit+1) + 2, 2)种方案
      • 有三个小朋友大于limit的方案 相当于把n - 3 * (limit + 1)和 2个挡板进行放置,2个挡板有C(n - 3 * (limit+1) + 2, 2)种方案
  • 解题代码:

class Solution {
public:
    long long distributeCandies(int n, int limit) {
        long long ans = 0;
        auto func = [&](long long num) -> long long
        {
            if(num < 0)
                return 0;
            return num * (num - 1) / 2;
        };
        return func(n+2) - 3 * func(n - (limit + 1) + 2) + 3 * func(n - 2 * (limit + 1) + 2) - func(n - 3 * (limit + 1) + 2);
    }
};

Q3 重新排列后包含指定子字符串的字符串数目

  • 题目链接

  • 解题思路:

    • 记忆化搜索
    • 定义dfs(i, l, e, t),i表示[0, i]范围的子字符串,l,e,t分别表示需要的l,e,t字符的数量
    • 状态转移:
      • 当前位置选择l,e,t之外的字符:23 * dfs(i-1, l, e, t)
      • 当前位置选择l :dfs(i-1, 0, e, t)
      • 当前位置选择e:dfs(i-1, l, max(e-1, 0), t)
      • 当前位置选择t:dfs(i-1, l, e, 0)
      • dfs(i, l, e, t) 的值为上面四项的和
    • 递归入口:dfs(n-1, 1, 2, 1)
  • 解题代码:


class Solution {
public:
    int stringCount(int n) {
        if(n < 4)
            return 0;
        const int MOD = 1e9+7;
        long long f[n][2][3][2];
        memset(f, -1, sizeof(f));
        function dfs = [&](int i, int lcnt, int ecnt, int tcnt) -> long long
        {
            if(i < 0)
                return lcnt == 0 && ecnt == 0 && tcnt == 0 ? 1 : 0;
            if(f[i][lcnt][ecnt][tcnt] != -1)
                return f[i][lcnt][ecnt][tcnt];
            long long &ans = f[i][lcnt][ecnt][tcnt];
            ans = 0;
            //随便选
            ans = (ans + 23 * dfs(i-1, lcnt, ecnt, tcnt)) % MOD;
            //作为leet中的一个字符来选
            ans = (ans + dfs(i-1, 0, ecnt, tcnt)) % MOD;
            ans = (ans + dfs(i-1, lcnt, max(ecnt-1, 0), tcnt)) % MOD;
            ans = (ans + dfs(i-1, lcnt, ecnt, 0)) % MOD;
            return ans;
        };
        return dfs(n-1, 1, 2, 1);
    }
};

  • 解题思路2:

    • 同样可以使用容斥原理

    • 总共有26 ^ n个字符串,减去不含leet的字符串个数

    • 不含leet的字符串需要至少满足下面三个条件中的一个:

      • 不含l
      • 不含t
      • 不含e或恰好包含一个e
    • 至少满足一个条件:

      • 不含l:25^n
      • 不含t:25^n
      • 不含e:25^n
      • 恰好包含一个e:n * 25^(n-1)
    • 至少满足两个条件:

      • 不含l和t:24^n
      • 不含l且e的个数不足两个:24^n + n * 24^(n-1)
      • 不含t且e的个数不足两个:24^n + n * 24^(n-1)
    • 满足三个条件:23^n + n * 23^(n-1)

    • 最终答案 = 总方案数 - 至少满足一个条件 + 至少满足两个条件 - 满足三个条件

    • 用快速幂计算幂

  • 解题代码:


class Solution {
public:
    const int MOD = 1e9+7;
    long long myPow(int a, int n)
    {
        if(n == 0)
            return 1;
        if(n & 1)
            return (a * myPow(a, n-1)) % MOD;
        long long temp = myPow(a, n/2);
        return (temp * temp) % MOD;
    }
    int stringCount(int n) {
        return ((myPow(26, n) - 
                  (myPow(25, n) * 3 + n * myPow(25, n-1)) + 
                  (myPow(24, n) * 3 + 2 * n * myPow(24, n-1)) - 
                  (myPow(23, n) + n * myPow(23, n-1))) % MOD + MOD) % MOD;
    }
};

Q4 购买物品的最大开销

  • 题目链接
  • 解题思路:
    • 用一个小根堆维护当前可取的物品
    • 每次都取价值最小的物品,并将其左侧物品(如果有)加入到堆中
  • 解题代码:

class Solution {
public:
    long long maxSpending(vector>& values) {
        //最大开销
        long long ans = 0;
        priority_queue, vector>, greater<>> pq;
        int m = values.size();
        int n = values[0].size();
        for(int i = 0; i < m; i++)
            pq.push({values[i][n-1], i * n + n-1});
       
        long long d = 1;
        while(!pq.empty())
        {
            auto [val, idx] = pq.top();
            pq.pop();
            ans += val * d;
            int x = idx / n;
            int y = idx % n;
            if(y != 0)
                pq.push({values[x][y-1], idx-1});
            d += 1;
        }
        return ans;
    }
};

你可能感兴趣的:(算法题题解,leetcode,算法)