1534 统计好三元组
文章目录
- [1534 统计好三元组](https://leetcode.cn/problems/count-good-triplets/description/)
- 题目
- 解法1 暴力穷举
- 解法2 枚举优化
题目
给你一个整数数组 arr
,以及 a
、b
、c
三个整数。请你统计其中好三元组的数量。
如果三元组 (arr[i], arr[j], arr[k])
满足下列全部条件,则认为它是一个 好三元组 。
0 <= i < j < k < arr.length
|arr[i] - arr[j]| <= a
|arr[j] - arr[k]| <= b
|arr[i] - arr[k]| <= c
其中 |x|
表示 x
的绝对值。
返回 好三元组的数量 。
示例 1:
输入:arr = [3,0,1,1,9,7], a = 7, b = 2, c = 3
输出:4
解释:一共有 4 个好三元组:[(3,0,1), (3,0,1), (3,1,1), (0,1,1)] 。
示例 2:
输入:arr = [1,1,2,2,3], a = 0, b = 0, c = 1
输出:0
解释:不存在满足所有条件的三元组。
提示:
3 <= arr.length <= 100
0 <= arr[i] <= 1000
0 <= a, b, c <= 1000
解法1 暴力穷举
最简单的方法就是暴力穷举,按照题目要求判断即可。
class Solution {
public:
int countGoodTriplets(vector<int>& arr, int a, int b, int c) {
int count = arr.size();
int match = 0;
for (int i =0; i < count - 2; ++i) {
auto& val_i = arr[i];
for (int j = i + 1; j < count - 1; ++j) {
auto& val_j = arr[j];
if(abs(val_i - val_j) <= a) {
for (int k = j + 1; k < count; ++k) {
auto& val_k = arr[k];
if(abs(val_j - val_k) <= b && abs(val_i - val_k) <= c) {
++match;
}
}
}
}
}
return match;
}
};
解法2 枚举优化
思路与算法
先看题解:
class Solution {
public:
int countGoodTriplets(vector<int>& arr, int a, int b, int c) {
int ans = 0, n = arr.size();
vector<int> sum(1001, 0);
for (int j = 0; j < n; ++j) {
for (int k = j + 1; k < n; ++k) {
if (abs(arr[j] - arr[k]) <= b) {
int lj = arr[j] - a, rj = arr[j] + a;
int lk = arr[k] - c, rk = arr[k] + c;
int l = max(0, max(lj, lk)), r = min(1000, min(rj, rk));
if (l <= r) {
if (l == 0) {
ans += sum[r];
}
else {
ans += sum[r] - sum[l - 1];
}
}
}
}
for (int k = arr[j]; k <= 1000; ++k) {
++sum[k];
}
}
return ans;
}
};
作者:力扣官方题解
链接:https://leetcode.cn/problems/count-good-triplets/solutions/371340/tong-ji-hao-san-yuan-zu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
首先,可以根据 ∣ a r r [ j ] − a r r [ k ] ∣ < = b |arr[j] - arr[k]| <= b ∣arr[j]−arr[k]∣<=b的限制条件,找到符合条件的(j,k)二元组
然后根据arr[i] 与 arr[j]、arr[k]的大小关系进行不等式变换
∣
a
r
r
[
i
]
−
a
r
r
[
j
]
∣
<
=
a
|arr[i] - arr[j]| <= a
∣arr[i]−arr[j]∣<=a
∣ a r r [ i ] − a r r [ k ] ∣ < = c |arr[i] - arr[k]| <= c ∣arr[i]−arr[k]∣<=c
推导出:
a
r
r
[
j
]
−
a
<
=
a
r
r
[
i
]
<
=
a
r
r
[
j
]
+
a
arr[j] - a <= arr[i] <= arr[j] + a
arr[j]−a<=arr[i]<=arr[j]+a
a
r
r
[
k
]
−
c
<
=
a
r
r
[
i
]
<
=
a
r
r
[
k
]
+
c
arr[k] - c <= arr[i] <= arr[k] + c
arr[k]−c<=arr[i]<=arr[k]+c
因此可以看出 arr[i]同时满足2个区间要求,因此肯定满足2区间的交集,根据左右区间的大小关系,可以得出相交区间为[l, r]
注意,这里 l 和 r 是 arr[i] 的值域范围。
因此,只要知道当$ arr[i]\in[l, r]$时,i 的个数即可。
这就是题解引入了 频次数组的前缀和 sum
看下sum的赋值
vector<int> sum(1001, 0);
for (int j = 0; j < n; ++j) {
......
for (int k = arr[j]; k <= 1000; ++k) {
++sum[k];
}
}
}
每次遍历 j 的时候,可以获取到 arr[j] 的值,根据arr[j] 的值更新 sum 数组
更新思维:sum 是 std::vector<int>(1001, 0),即 1001个值为0的数组,每当读入1个 arr[j],表示从下标 k = arr[j] 开始,sum 中的值均要+1,也就是在sum中,下标0~(k-1)对应的值没有增加,因为当前 arr[j] 并不小于 0~k-1,而当 下标 >= k(arr[j])时,arr[0]~arr[j] 中数值<=下标的个数才会增加
也就是对于数组来说,每次j 向前1步,通过读取sum[t],就能知道 arr[i] <= t的个数有多少 , sum[t]表示当前数组中,arr[i] <= t 的个数有 sum[t]个
解题思路确实别具一格,减少了1次内嵌for循环,通过遍历 j 的时候,不断更新数组中 小于等于 arr[j] 的个数,更新到 sum 数组中,从而每次根据计算出来的 [l, r],直接从数组中读取 arr[i]的个数,节约了时间。