持续创作,加速成长!这是我参与「掘金日新计划 · 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;
}
}