Leetcode周赛365总结

Q1 有序三元组中的最大值I

  • 题目链接

  • 解题思路:

    • 三重循环暴力
    • 初始化答案为ans
    • 枚举i、j、k三个下标,分别计算三元组的值,如果大于ans则更新ans
  • 解题代码:

class Solution {
public:
    long long maximumTripletValue(vector<int>& nums) {
        long long ans = 0;
        int n = nums.size();
        for(int i = 0; i < n; i++)
        {
            for(int j = i+1; j < n; j++)
            {
                for(int k = j+1; k < n; k++)
                    ans = max(ans, ((long long)nums[i] - nums[j]) * nums[k]);
            }
        }
        return ans;
    }
};

Q2 有序三元组中的最大值II

  • 题目链接

  • 解题思路:

    • 枚举j的位置,可以得到的最大分数是(j左侧最大值 - nums[j]) * j右侧最大值
  • 解题代码:

class Solution {
public:
    long long maximumTripletValue(vector<int>& nums) {
        long long ans = 0;
        /*
            枚举j
            (左侧最大值 - nums[j]) * 右侧最大值
        */
        int n = nums.size();
        vector<int> rightMin(n, 0), rightMax(n, 0);
        rightMax[n-1] = 0;
        for(int i = n-2; i >= 0; i--)
            rightMax[i] = max(rightMax[i+1], nums[i+1]);
        long long leftMax = nums[0];
        for(int j = 1; j < n; j++)
        {    
            ans = max(ans, (leftMax - nums[j]) * rightMax[j]);
            leftMax = max(leftMax, (long long)nums[j]);
        }
        return ans;
    }
};


  • 另一种思路:
    • 枚举k
    • 需要维护nums[i] - nums[j]的最大值 max_diff
    • 把当前枚举数当作 nums[j],还需要知道左侧的最大值pre_max
    • 维护的方式类似Leetcode121.买卖股票的最佳时机
  • 解题代码:
class Solution {
public:
    long long maximumTripletValue(vector<int>& nums) {
        long long ans = 0;
        long long max_diff = 0, pre_max = 0;
        for(auto num : nums)
        {
            //先当作nums[k]
            ans = max(ans, max_diff * num);
            //再当作nums[j]
            max_diff = max(max_diff, pre_max - num);
            //再当作nums[i]
            pre_max = max(pre_max, (long long)num);
        }
        return ans;
    }
};

Q3 无限数组的最短子数组

  • 题目链接

  • 解题思路:

    • 从infinite_nums中找出满足元素和等于target的子数组存在两种情况:
      • 子数组在原始数组内部
      • 子数组横跨了多个原始数组,可以表示成 原数组一个后缀和 + (0 - n)个原始数组和 + 原数组一个前缀和
    • 先求原始数组所有元素和sum
    • 将原始数组重复两遍,在新的数组中找是否有子数组sub其和满足(target % sum) ,则和为target的子数组长度为sub长度 + target/sum * 原始数组长度
  • 解题代码:

class Solution {
public:
    int minSizeSubarray(vector<int>& nums, int target) {
        int ans = INT_MAX;
        int n = nums.size();
        long long sum = accumulate(nums.begin(), nums.end(), 0ll);
        int cnt = target / sum;
        target %= sum;
        if(target == 0)
            return cnt * n;
        sum = 0;
        unordered_map<int, int> um;
        um[0] = -1;
        for(int i = 0; i < 2*n; i++)
        {
            sum += nums[i%n];
            if(um.count(sum - target))
                ans = min(ans, i - um[sum-target] + cnt * n);
            um[sum] = i;
        }
        return ans < INT_MAX ? ans : -1;
    }
};

  • 可以使用滑动窗口优化到需要O(1)空间
  • 解题代码:
class Solution {
public:
    int minSizeSubarray(vector<int>& nums, int target) {
        int ans = INT_MAX;
        int n = nums.size();
        long long sum = accumulate(nums.begin(), nums.end(), 0ll);
        int left = 0;
        long long s = 0;
        for(int right = 0; right < 2*n; right++)
        {
            s += nums[right % n];
            while(s > target % sum)
            {
                s -= nums[left % n];
                left += 1;
            }
            if(s == target % sum)
                ans = min(ans, right-left+1);
        }
        if(ans == INT_MAX)
            return -1;
        return ans + (target / sum) * n;
    }
};

Q4 有向图访问计数

  • 题目链接

  • 解题思路:

    • 基环树(内向基环树)
    • 先用拓扑排序把环以外的树枝全部删去,此时剩下的就是基环部分,这里注意可能存在多个基环
    • 遍历每一个基环上的每个点,该点可到达的节点数为其所在的基环长度
      • 以该点为根进行dfs,其子树上的节点可以到达的节点数为子树上节点到该节点距离 + 基环长度
  • 解题代码:

class Solution {
public:
    vector<int> countVisitedNodes(vector<int>& edges) {
        int n = edges.size();
        vector<int> deg(n, 0);
        vector<vector<int>> g(n);
        for(int i = 0; i < n; i++)
        {    
            deg[edges[i]] += 1;
            g[edges[i]].push_back(i);
        }
        //拓扑排序
        queue<int> q;
        for(int i = 0; i < n; i++)
        {
            if(deg[i] == 0)
                q.push(i);
        }
        while(!q.empty())
        {
            int cur = q.front();
            q.pop();
            deg[edges[cur]] -= 1;
            if(deg[edges[cur]] == 0)
                q.push(edges[cur]);
        }
        vector<int> ans(n);
        function<void(int, int)> dfs = [&](int x, int depth)
        {
            ans[x] = depth;
            for(auto y : g[x])
            {
                if(deg[y] == 0)
                    dfs(y, depth + 1);
            }
        };
        //此时deg不为0的全是在基环上的点
        for(int i = 0; i < n; i++)
        {
            if(deg[i] == 0)
                continue;
            vector<int> ring;
            int cur = i;
            while(deg[cur] > 0)
            {
                ring.push_back(cur);
                deg[cur] = -1;
                cur = edges[cur];
            }
            //此时ring当中是一个基环
            //遍历基环中的每一个点
            int len = ring.size();
            for(int j = 0; j < len; j++)
                dfs(ring[j], len);
        }  
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值