题目
LEETCODE 2681
简单来说,就是定义力量值为若干个整数中最大值的平方乘以最小值,然后给一个整数数组,要求算出其中所有的非空组合的力量值的和,结果需要取模。
算法描述
首先分析一下,这种题目的套路通常是算“贡献”,即分开进行计数统计。
然后不难发现与顺序无关,所以按从小到大排序后,以每个数作为组合中的最大值的贡献入手,只需要求出小于等于当前数的数字中,所有包含当前数的组合的最小值之和,再乘以当前数的平方,即为贡献,最后将各个贡献相加即可求得答案。
分析到这里,关键问题在于如何求小于等于当前数的数字中所有包含当前数的组合的最小值之和。由于排过序了,更简便且精确的表述是前
i
i
i个数的所有包含第
i
i
i个数的组合的最小值之和。不难想到用递推/动态规划的思想去考虑。记给定的已排序的数组为
a
a
a,设
f
(
i
)
f(i)
f(i)表示前
i
i
i个数中所有非空组合(不一定包含第
i
i
i个数)的最小值之和,那么
f
(
0
)
=
0
f(0)=0
f(0)=0,
f
(
1
)
=
a
[
1
]
f(1)=a[1]
f(1)=a[1],再分析一下递推关系,组合中不包含
a
[
i
]
a[i]
a[i]的话结果为
f
(
i
−
1
)
f(i-1)
f(i−1),包含
a
[
i
]
a[i]
a[i]的话,考虑一下非空组合的问题,结果应为
f
(
i
−
1
)
+
a
[
i
]
f(i-1)+a[i]
f(i−1)+a[i],综上,递推公式如下:
f
(
i
)
=
2
×
f
(
i
−
1
)
+
a
[
i
]
f(i) = 2 \times f(i-1)+a[i]
f(i)=2×f(i−1)+a[i]
而我们关心的是前
i
i
i个数的所有包含第
i
i
i个数的组合的最小值之和,这通过
f
(
i
)
f(i)
f(i)并不难求,即
f
(
i
−
1
)
+
a
[
i
]
f(i-1)+a[i]
f(i−1)+a[i]。
所以,第
i
i
i个数作为组合中的最大值产生的贡献即为
a
[
i
]
2
×
(
f
(
i
−
1
)
+
a
[
i
]
)
a[i]^{2} \times (f(i-1)+a[i])
a[i]2×(f(i−1)+a[i]),累加即可得答案。
分析一下复杂度:
时间复杂度:递推是
O
(
n
)
O(n)
O(n)的,但是排序是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的,所以时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn);
空间复杂度:后面的递推可以使用滚动数组,空间可以优化到常数级,但是排序需要用到
O
(
l
o
g
n
)
O(logn)
O(logn)的递归调用栈空间。
需要注意一下,写法上的细节问题,如位运算、滚动数组、取模等,参照代码实现。
代码实现
typedef long long ll;
const ll P = 1e9 + 7;
class Solution {
public:
int sumOfPower(vector<int>& nums) {
int n = nums.size();
vector<ll> f;
f.resize(2);
int ans = 0;
sort(nums.begin(), nums.end());
for (int i = 0; i < n; i++) {
int ii = (i&1);
f[ii] = (f[ii^1] << 1) % P;
f[ii] = (f[ii] + nums[i]) % P;
ll tmp = 1LL * nums[i] * nums[i] % P;
ll tmp1 = (f[ii^1] + nums[i]) % P;
tmp = tmp * tmp1 % P;
ans = ((ll)ans + tmp) % P;
}
return ans;
}
};
关键词
力扣 每日一题 英雄的力量 2681 动态规划 前缀和 递推