Leetcode周赛专题

204场周赛

5500. 乘积为正数的最长子数组长度

解题思路:

我们使用两个数组来进行动态规划,分别为p_dp[i]:以 i 结尾乘积为正数得最长子数组长度 n_dp[i]:以 i 结尾乘积为负数得最长子数组长度。当nums[i]为不同情况时,每个数组得不同操作如下:

  • nums[i] > 0时,p_dp[i] = p_dp[i - 1] + 1即可。n_dp[i] 要根据n_dp[i - 1] 是否存在负数得最长子数组,若存在则乘上一个正数还是负数所以n_dp[i] = n_dp[i - 1] + 1,若不存在则n_dp[i] = 0
  • nums[i] < 0时,p_dp[i]要根据n_dp[i - 1],因为若以i - 1结尾乘积为负数得最长子数组长度乘上一个负数,则肯定为以 i 结尾乘积为正数得最长子数组长度。同样得道理,n_dp[i] 根据 p_dp[i - 1]乘上一个负数得到最长子数组长度。
  • nums[i] == 0时,p_dp和n_dp都不会选择得,所以全部重置为0即可

代码:

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        int sz = nums.size();
        vector<int> p_dp(sz);
        vector<int> n_dp(sz);
        p_dp[0] = nums[0] > 0 ? 1 : 0;
        n_dp[0] = nums[0] < 0 ? 1 : 0;
        int res = p_dp[0];
        for(int i = 1; i < sz; i++) {
            if(nums[i] > 0) {
                p_dp[i] = p_dp[i - 1] + 1;
                n_dp[i] = n_dp[i - 1] > 0 ? n_dp[i - 1] + 1 : 0;
            } else if(nums[i] < 0) {
                p_dp[i] = n_dp[i - 1] > 0 ? n_dp[i - 1] + 1 : 0;
                n_dp[i] = p_dp[i - 1] + 1;
            } else {
                p_dp[i] = 0;
                n_dp[i] = 0;
            }
            res = max(res , p_dp[i]);
        }
        return res;
    }
};

总结:

  1. 这里得dp数组每个都是局部求解,因为在遇到0时,我们全部重置了,重置后得结果可能不是最优解,所以我们不能直接返回p_dp[sz - 1],要使用res单独存储起来,每次都更新。
  2. 这题得难点不在于动态规划得思想,在于两个不同状态得切换求值,当nums[i] > 0 或者nums[i] < 0时,如果通过另一个状态来求得该状态得值。我们只是把两种状态使用数组表示而已

 

1568. 使陆地分离的最少天数

解题思路:

设置check函数判断是否只存在一个岛屿,我们只要检查是否删除一个陆地后都都会存在一个岛屿,那么我们只要找到一个相邻有4条边得陆地,删除以它和它得对角一个陆地,即可分离出两个岛屿了。

代码:

class Solution {
public:
    int dx[4] = {1 , 0 , -1 , 0};
    int dy[4] = {0 , 1 , 0 , -1};

    bool isInside(const vector<vector<int>>& grid , int x , int y) {
        return x >= 0 && x < grid.size() && y >= 0 && y < grid[0].size();
    }

    bool check(const vector<vector<int>>& grid) {
        int x , y , count = 0;
        for(int i = 0; i < grid.size(); i++) {
            for(int j = 0; j < grid[i].size(); j++) {
                if(grid[i][j] == 0)     continue;
                count++;
                x = i;
                y = j;
            }
        }
        queue<pair<int , int>> q;
        bool mark[30][30] = {0};
        q.push(make_pair(x , y));
        mark[x][y] = true;
        count--;
        while(!q.empty()) {
            auto f = q.front();
            q.pop();
            for(int i = 0; i < 4; i++) {
                int nx = f.first + dx[i] , ny = f.second + dy[i];
                if(isInside(grid , nx , ny) && grid[nx][ny] == 1) {
                    if(mark[nx][ny])    continue;
                    mark[nx][ny] = true;
                    q.push(make_pair(nx , ny));
                    count--;
                }
            }
        }
        return count != 0;
    }


    int minDays(vector<vector<int>>& grid) {
        if(check(grid))     return 0;
        for(int i = 0; i < grid.size(); i++) {
            for(int j = 0; j < grid[i].size(); j++) {
                if(grid[i][j] == 0)     continue;
                grid[i][j] = 0;
                if(check(grid))     return 1;
                grid[i][j] = 1;
            }
        }
        return 2;
    }
};

总结:

这题更像是一道思维题,当发现最多只要2步操作得时候,这题就AC了。这道题得时间复杂度为 O(n^4)

 

1569. 将子数组重新排序得到同一个二叉查找树的方案数

代码:

class Solution {
public:
    const long long mod = 1000000007;
    long long com[1001][1001];
    long long combine(int a , int b) {
        return com[a][b];
    }

    void dfs(vector<int>& nums , int L , int R , long long &mul) {
        if(R - L + 1 <= 2)  return;
        vector<int> less , greater;
        for(int i = L + 1; i <= R; i++) {
            if(nums[i] < nums[L])   less.push_back(nums[i]);
            else    greater.push_back(nums[i]);
        }
        mul *= combine(less.size() + greater.size() , greater.size());
        if(mul >= mod)   mul %= mod;
        dfs(less , 0 , less.size() - 1 , mul);
        dfs(greater , 0 , greater.size() - 1 , mul);
    }

    int numOfWays(vector<int>& nums) {
        com[1][0] = com[1][1] = 1;
        for(int i = 2; i <= 1000; i++) {
            com[i][0] = 1;
            for(int j = 1; j <= i; j++) {
                com[i][j] = (com[i - 1][j] + com[i - 1][j - 1]) % mod;
            }
        }
        long long mul = 1;
        dfs(nums , 0 , nums.size() - 1, mul);
        return (mul - 1 + mod) % mod;
    }
};

 

34场双周赛

5492. 分割字符串的方案数

解题思路:

对于没有1的字符串,就是排列组合问题,一共有 种可能。

对于有1的字符串,若1的数量不能被3整除时,直接返回0。否则就是计算第一个字符串结尾和第二个字符开头有多少个0字符串再加1,再计算第二个字符串结尾和第三个字符串开发之间有多少个0字符串再加1。两数相乘即可得到答案

代码:

class Solution {
public:
    const int mod = 1000000007;
    int numWays(string s) {
        bool flag = true;
        long long res = 0;
        int num = 0;
        for(int i = 0; i < s.size(); i++) {
            if(s[i] == '1') {
                flag = false;
                num++;
            }
        }
        if(flag) {
            long long sz = s.size();
            res =  (sz - 1) * (sz - 2) / 2;
            return res % mod;
        }
        if(num % 3 != 0) {
            return 0;
        }
        int both = num / 3;
        long long temp1 = 1 , temp2 = 1 , temp = 0;
        for(int i = 0; i < s.size(); i++) {
            if(s[i] == '1' && both) {
                both--;
            } else if(s[i] == '0' && !both) {
                temp++;
            } else if(s[i] == '1' && !both) {
                if(temp1 == 1)  temp1 = temp + 1;
                else if(temp2 == 1)  temp2 = temp + 1;
                temp = 0;
                both = num / 3 - 1;
            }
        }
        res = temp1 * temp2;
        res %=  mod;
        return res;
    }
};

总结:

这题在我比赛时,死在了没有1的情况下,不知道如何算出,我直接使用加法进行计算。

 

5493. 删除最短的子数组使剩余数组有序

解题思路:

  1. 找到两端的有序子数组
  2. 合并两个数组成为一个新的有序数组,计算影响成为有序数组数据的数量
  3. 计算无序数组的数量
  4. 返回影响成为有序数组数据的数量+无序数组的数量

代码:

class Solution {
public:
    int findLengthOfShortestSubarray(vector<int>& arr) {
        int n = arr.size();
        int i = 0;
        for(; i < n - 1; i++) {
            if(arr[i] > arr[i + 1])     break;
        }
        int left = i;
        if(left == n - 1)   return 0;
        i = n - 1;
        for(; i > 0; i--) {
            if(arr[i] < arr[i - 1])   break;
        }
        int right = i;
        if(right - left == n - 1) {
            if(arr[right] >= arr[left])     return n - 2;
            return n - 1;
        }
        if(arr[left] <= arr[right])     return right - left - 1;
        int l = 0 , r = right , cnt = 0;
        while(l < left + 1 && r < n) {
            if(arr[l] > arr[r]) {
                cnt++;
                l++;
                r++;
            } else {
                l++;
            }
        }
        return right - left - 1 + cnt;
    }
};

总结:

  1. left == n - 1,证明数组本身就是个有序数组,直接返回0
  2. right - left == n - 1,证明数组除开头尾就是一个反序数组。然后判断arr[right]和arr[left]
    1. 若arr[left]和arr[right]能组成一个有序数组,则返回n - 2
    2. 否则返回n - 1
  3. 当arr[left] <= arr[right],证明左端有序数组的结尾与右端有序数组的开头能组成有序数组,直接删除中间的无需数组即可

 

5494. 统计所有可行路径

解题思路:

每种求路径数量的问题都可以使用动态规划来解决。问题就是如何设置dp数组和函数逻辑。

dp[i][j]表示当前起点为i,剩余油量j的总方案数。

因为这个路径可以重复走无数次,所以我们的结束条件就是不重复计算即可。

代码:

typedef long long ll;
int dp[105][205];
const int mod = 1e9 + 7;

class Solution {
public:
    int dfs(vector<int>& locations , int start , int finish , int fuel) {
        if(dp[start][fuel] != -1)      return dp[start][fuel];
        ll res = 0;
        if(start == finish)     res++;
        int len = locations.size();
        for(int i = 0; i < len; i++) {
            int diff = abs(locations[i] - locations[start]);
            if(start == i)      continue;
            if(fuel - diff >= 0) {
                res = (res + dfs(locations , i , finish , fuel - diff)) % mod;
            }
        }
        dp[start][fuel] = res % mod;
        return dp[start][fuel];
    }

    int countRoutes(vector<int>& locations, int start, int finish, int fuel) {
        memset(dp , -1 , sizeof(dp));
        return dfs(locations , start , finish , fuel);
    }
};

总结:

  1. 必须要在类内初始化一下,如果不初始化,默认数组中的数就为0,但是dp[start][fuel] == 0,可能会出现重复计算的情况

这题其实不难,或者说动态规划的题大部分都不难,主要是找到dp数组的参数设置,然后就是空间和时间的优化。这些都是动态规划中涉及的。

 

第205场周赛

1577. 数的平方等于两数乘积的方法数

解题思路:

直接用两个哈希表来存储数组中每个数的平方值的个数,最后根据nums1[i] * nums1[j]的值为索引值找到个数,nums2同理可得

代码:

class Solution {
public:
    int numTriplets(vector<int>& nums1, vector<int>& nums2) {
        map<long long , int> mli1 , mli2;
        int res = 0;
        for(auto i : nums1)  mli1[(long long) 1 * i * i]++;
        for(auto i : nums2)  mli2[(long long) 1 * i * i]++;
        for(int i = 0; i < nums1.size(); i++) {
            for(int j = i + 1; j < nums1.size(); j++) {
                res += mli2[(long long) 1 * nums1[i] * nums1[j]];
            }
        }
        for(int i = 0; i < nums2.size(); i++) {
            for(int j = i + 1; j < nums2.size(); j++) {
                res += mli1[(long long) 1 * nums2[i] * nums2[j]];
            }
        }
        return res;
    }
};

总结:

这题使用哈希表存储平方数值的个数方法解决,比赛时想到用的是递归方法,想得非常的复杂,其实是道水题

1578. 避免重复字母的最小删除成本

解题思路:

当遇到重复字母时,我们使用贪心思想,选择当前两个中的较小成本的字母进行”删除“。当“删除”的字母是i + 1时,因为i + 1可能会影响到后面的成本计算,所以把i + 1和 i 的成本交换一下,相当于删除的是永远是前面一个重复数,并且是成本较小的。

代码:

class Solution {
public:
    int minCost(string s, vector<int>& cost) {
        int res = 0 , n = s.size();
        for(int i = 0; i < n - 1; i++) {
            if(s[i] == s[i + 1]) {
                res += min(cost[i] , cost[i + 1]);
                if(cost[i] > cost[i + 1])   swap(cost[i] , cost[i + 1]);
            }
        }
        return res;
    }
};

总结:

这里的贪心思想如何转换成对于全局都适用的规律呢,就是swap(cost[i] , cost[i + 1])。当我们遇到3个或者多个相同的数时,我总是比较当前的两个,无法想到如何顾及到后面较小的成本值。这就是解决方法。

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值