LeetCode: 1395. 统计作战单位数

2023-10-13 01:52

本文主要是介绍LeetCode: 1395. 统计作战单位数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1. 解法一:枚举中点

2. 解法二:树状数组 + 离散化 优化解法一


原题链接:1395. 统计作战单位数 - 力扣(LeetCode)

题目描述:

n 名士兵站成一排。每个士兵都有一个 独一无二 的评分 rating 。

每 3 个士兵可以组成一个作战单位,分组规则如下:

  • 从队伍中选出下标分别为 ijk 的 3 名士兵,他们的评分分别为 rating[i]rating[j]rating[k]
  • 作战单位需满足: rating[i] < rating[j] < rating[k] 或者 rating[i] > rating[j] > rating[k] ,其中  0 <= i < j < k < n

请你返回按上述条件可以组建的作战单位数量。每个士兵都可以是多个作战单位的一部分。

示例 1:

输入:rating = [2,5,3,4,1]
输出:3
解释:我们可以组建三个作战单位 (2,3,4)、(5,4,1)、(5,3,1) 。

示例 2:

输入:rating = [2,1,3]
输出:0
解释:根据题目条件,我们无法组建作战单位。

示例 3:

输入:rating = [1,2,3,4]
输出:4

提示:

  • n == rating.length
  • 3 <= n <= 1000
  • 1 <= rating[i] <= 10^5
  • rating 中的元素都是唯一的

1. 解法一:枚举中点

要找到有多少个满足条件的三元组,我们可以枚举三元组的中间位置,通过统计中间位置两侧满足条件的元素的数量来解决这个问题。

对于满足条件的三元组有以下两种可能:

1:随着下标的增长,三元组严格递增。在枚举中间位置时,我们就需要统计出中间位置左侧比中间位置元素小的元素个数,再统计出中间位置右侧比中间位置大的元素个数。两者的乘积就是这个中间位置,三元组严格递增的数目。

例如:2,1,3,5,4  当我们枚举到 3 时,统计 3 的左侧小于 3 的元素个数:2,  统计 3 的右侧大于 3 的元素个数:2。那么,以 3 为中间位置,严格递增的三元组数目就是 2 * 2 = 4 (一个简单的组合问题)。

2:随着下标的增长,三元组严格递减。原理和上面是一样的哇!我们需要统计出中间位置左侧比中间位置元素大的元素个数,再统计出中间位置右侧比中间位置小的元素个数。两者的乘积就是这个中间位置,三元组严格递减的数目。

例如:6,5,3,4,2  当我们枚举到 3 时,统计 3 的左侧大于 3 的元素个数:2,  统计 3 的右侧小于 3 的元素个数:1。那么,以 3 为中间位置,严格递减的三元组数目就是 2 * 1 = 2 (一个简单的组合问题)。

重复上述操作,枚举数组中的所有可能的中间位置,计算每个位置的满足条件的三元组个数,求和即可!

 

class Solution {
public:int numTeams(vector<int>& rating) {int n = rating.size();int ans = 0;for (int j = 1; j < n - 1; ++j) {//左侧小于      左侧大于int iless = 0, imore = 0;//右侧小于      右侧大于int kless = 0, kmore = 0;for (int i = 0; i < j; ++i) {if (rating[i] < rating[j]) {++iless;}else if (rating[i] > rating[j]) {++imore;}}for (int k = j + 1; k < n; ++k) {if (rating[k] < rating[j]) {++kless;}else if (rating[k] > rating[j]) {++kmore;}}ans += iless * kmore + imore * kless;}return ans;}
};

时间复杂度分析:我们在枚举中间位置的时候需要向左和向右遍历数组找到较大和较小元素的个数。因此时间复杂度是:O(N * N)。空间复杂度: O(1)。

2. 解法二:树状数组 + 离散化 优化解法一

在解法二中,我们枚举每个中间位置时,都需要向左和向右找比中间位置大和小的元素个数。这个过程能否优化呢?

我们可以创建一个数组arr, 初始化为0,遍历到一个数字num,就让arr[num] = 1。
如上图:我们遍历到 3 时 2,5,已经出现过了,那么arr[2] 和 arr[5] 就是 1, 
我们要找左侧比 3 小的元素,只需要知道下标为 3 的位置的前缀和即可(如图可知前缀和为1)
我们要找左侧比 3 大的元素,只需要知道整个数组的前缀和(如图可知前缀和为2),然后减去下标为 3 的位置的前缀和(如图可知前缀和为 1 ) 即可:2 - 1 = 1。
计算出结果之后,再让 arr[3] = 1, 代表 3 出现过了!
通过这样的方法我们就可以在较快时间内求出,左侧较小的元素个数,左侧较大的元素个数。

求左侧较小元素的个数与左侧较大元素的个数需要从左向右遍历原数组,求右侧较小元素的个数与右侧较大元素的个数就需要从右向左遍历原数组啦! 

这里的这个 arr 数组如果用普通的前缀和数组,时间复杂度还是没有变,因为我们需要添加新的数到前缀和数组。涉及到单点更新的前缀和维护自然就是树状数组啦!

我们还注意到,原数组的大小只有 1000,但是数组中的元素大小有 10^5 级别的,因此我们可以考虑使用整数的离散化,减小树状数组的高度,优化单点修改和区间查询的效率。

整数保序的离散化(C/C++)_c++离散化-CSDN博客

这是离散化的讲解,树状数组与线段树还没写!如果不熟悉可以用其他大佬的博客复习复习。

 

const int N = 1010; //离散化之后的最大数据范围
int tr[N]; //树状数组
class Solution {
public://树状数组的三个函数int lowbit(int x){return x & -x;}//这道题目的add仅仅是加一,就不用多谢一个参数啦void add(int x){for(int i = x; i <= N; i += lowbit(i))tr[i] += 1;}int query(int x){int res = 0;for(int i = x; i > 0; i -= lowbit(i))res += tr[i];return res;}//离散化需要用到的二分查找,有库函数我不用,唉就是玩儿int BinarySearch(vector<int>& nums, int target){int l = 0, r = nums.size() - 1;while(l <= r){int mid = (l + r) >> 1;if(nums[mid] < target) l = mid + 1;else if(nums[mid] > target) r = mid - 1;else return mid + 1;}return -1;}int numTeams(vector<int>& rating) {int n = rating.size();//copy数组用来离散化vector<int> copy(rating);//题目说了没有重复元素,不需要去重sort(copy.begin(), copy.end());//记录左右两侧较大和较小的元素个数vector<int> iless(n);vector<int> ibig(n);vector<int> kless(n);vector<int> kbig(n);int index = 0;//初始化树状数组memset(tr, 0, sizeof tr);//从前向后遍历找左侧for(int i = 0; i < n; i++){//查找离散化之后的结果index = BinarySearch(copy, rating[i]);//左侧较小iless[i] = query(index);//右侧较大ibig[i] = query(1000) - query(index);//加入新的数add(index);}//初始化memset(tr, 0, sizeof tr);//从后往前遍历找右侧for(int i = n - 1; i >= 0; i--){//同上index = BinarySearch(copy, rating[i]);kless[i] = query(index);kbig[i] = query(1000) - query(index);add(index);}//计算结果!int ans = 0;for(int i = 0; i < n; i++){ans += iless[i] * kbig[i] + ibig[i] * kless[i];}return ans;}
};

 时间复杂度分析:树状数组单点修改与区间查询的时间复杂度都是 O(logN), 因此最终的时间复杂度:O(N*logN),空间复杂度:O(N).

 

 

这篇关于LeetCode: 1395. 统计作战单位数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/199977

相关文章

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

hdu1496(用hash思想统计数目)

作为一个刚学hash的孩子,感觉这道题目很不错,灵活的运用的数组的下标。 解题步骤:如果用常规方法解,那么时间复杂度为O(n^4),肯定会超时,然后参考了网上的解题方法,将等式分成两个部分,a*x1^2+b*x2^2和c*x3^2+d*x4^2, 各自作为数组的下标,如果两部分相加为0,则满足等式; 代码如下: #include<iostream>#include<algorithm

poj 1981 单位圆套最多点

题意: 给n(300)个点,用单位圆去套他们,问最多能套多少个点。 解析: 点击打开链接 直接当作单位圆套最多点的模板吧,用极脚来排序。 代码: #pragma comment(linker, "/STACK:1677721600")#include <map>#include <set>#include <cmath>#include <queue>

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

hdu4267区间统计

题意:给一些数,有两种操作,一种是在[a,b] 区间内,对(i - a)% k == 0 的加value,另一种操作是询问某个位置的值。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import

hdu4417区间统计

给你一个数列{An},然后有m次查询,每次查询一段区间 [l,r] <= h 的值的个数。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamRead

hdu3333区间统计

题目大意:求一个区间内不重复数字的和,例如1 1 1 3,区间[1,4]的和为4。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;

hihocoder1114 小Hi小Ho的惊天大作战:扫雷·一

1114 : 小Hi小Ho的惊天大作战:扫雷·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 故事背景:密室、监视器与充满危机的广场 “我们还是循序渐进,先来考虑这样一个简单化问题:”小Hi思索片刻,道:“在一个大小为2*N的广场,其中第一行里的某一些格子里可能会有至多一个地雷,而第二行的格子里全都为数字,表示第一行中距离与这个格子不超过2的格子里总共有多少个