刷题的日常-统计作战单位数

99 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情

刷题的日常

一天一题,保持脑子清爽

统计作战单位数

来自leetcode的1395题,题意如下:

 n 名士兵站成一排。每个士兵都有一个 独一无二 的评分 rating 。
 每 3 个士兵可以组成一个作战单位,分组规则如下:
 从队伍中选出下标分别为 i、j、k 的 3 名士兵,他们的评分分别为 rating[i]、rating[j]、rating[k]
 作战单位需满足: rating[i] < rating[j] < rating[k] 或者 rating[i] > rating[j] > rating[k] ,其中  0 <= i < j < k < n
 请你返回按上述条件可以组建的作战单位数量。每个士兵都可以是多个作战单位的一部分。

理解题意

整理题目的信息,可以提取出条件如下:

  • 给出一个数组 rating,rating中的数不会重复,即每个数都是唯一的
  • 现在需要选出三个数,它们要满足如下条件之一
    • 三个数严格递减
    • 三个数严格递增
  • 返回满足条件的匹配的数量
  • 不允许改变士兵的相对位置

做题思路

根据条件,最简单的做法自然就是暴力解,三层循环,如果满足条件,就将结果加一,然后返回统计结果即可。但是这种做法会导致超时。
既然暴力不行,那么就换一种做法。根据题意,我们可以发现如下规律:

  • 假设我们遍历数组,当前遍历到i位
  • 以i结尾的满足条件的数量为 之前遍历过的小于i位的数(假设为j) 并且 以j结尾的递增或递减的数量为2 很明显,当前的状态可以根据之前遍历过的状态进行推断,那么就可以用动态规划做,如下:
  • 开辟空间记录已经遍历过的状态
  • 遍历数组 rating
  • 当前状态为 前面的所有小于或大于 当前值的数量加一
  • 汇总数量达到三的所有状态
  • 返回汇总数

代码实现

实现代码如下,需要用Team结构保存遍历的状态,两层循环,时间复杂度为O(n^2):

public class Solution {
    public int numTeams(int... rating) {
        Team[] dp = new Team[rating.length];
        int result = 0;
        for (int i = 0; i < rating.length; i++) {
            int item = rating[i];
            Team team = new Team();
            dp[i] = team;
            for (int j = 0; j < i; j++) {
                if (rating[j] > item) {
                    team.minSecond++;
                    team.minThird += dp[j].minSecond;
                    continue;
                }
                team.maxSecond++;
                team.maxThird += dp[j].maxSecond;
            }
            result += (team.maxThird + team.minThird);
        }
        return result;
    }

    static class Team {
        int minSecond;
        int minThird;
        int maxSecond;
        int maxThird;
    }
}

image.png