LeetCode-1995-统计特殊四元组
题目
给你一个 下标从 0 开始 的整数数组 nums ,返回满足下述条件的 不同 四元组 (a, b, c, d) 的 数目 :
nums[a] + nums[b] + nums[c] == nums[d] ,且 a < b < c < d
示例
输入:nums = [1,2,3,6]
输出:1
解释:满足要求的唯一一个四元组是 (0, 1, 2, 3) 因为 1 + 2 + 3 == 6 。
输入:nums = [3,3,6,4,5]
输出:0
解释:[3,3,6,4,5] 中不存在满足要求的四元组。
输入:nums = [1,1,1,3,5]
输出:4
解释:满足要求的 4 个四元组如下:
- (0, 1, 2, 3): 1 + 1 + 1 == 3
- (0, 1, 3, 4): 1 + 1 + 3 == 5
- (0, 2, 3, 4): 1 + 1 + 3 == 5
- (1, 2, 3, 4): 1 + 1 + 3 == 5
解答
思路1
暴力枚举,结果通过了??
思路2
来自三叶姐姐的解题思路,利用等式关系 nums[a] + nums[b] + nums[c] = nums[d]nums[a]+nums[b]+nums[c]=nums[d],具有明确的「数值」和「个数」关系,可将问题抽象为组合优化问题求方案数。
限制组合个数的维度有两个,均为「恰好」限制,转换为「二维费用背包问题求方案数」问题。
定义 f[i][j][k]
为考虑前 i 个物品(下标从 1 开始),凑成数值恰好 j,使用个数恰好为 k 的方案数。
最终答案为 $$\sum_{i = 3}^{n - 1}(f[i][nums[i]][3])$$起始状态 f[0][0][0]=1
代表不考虑任何物品时,所用个数为 0,凑成数值为 0 的方案数为 1。
不失一般性考虑 $$f[i][j][k]$$ 该如何转移,根据 $$nums[i - 1]$$ 是否参与组合进行分情况讨论:
nums[i - 1] 不参与组成,此时有:$$f[i - 1][j][k]$$ nums[i - 1] 参与组成,此时有:$$f[i - 1][j - t][k - 1]$$ 最终 $$f[i][j][k]$$为上述两种情况之和,最终统计 $$\sum_{i = 3}^{n - 1}(f[i][nums[i]][3])$$即是答案。
代码
int countQuadruplets(vector<int> &nums) {
int ans = 0;
for (int i = 0; i < nums.size() - 3; i++) {
for (int j = i + 1; j < nums.size() - 2; j++) {
for (int k = j + 1; k < nums.size() - 1; k++) {
for (int m = k + 1; m < nums.size(); m++) {
if (nums[i] + nums[j] + nums[k] == nums[m]) {
ans += 1;
}
}
}
}
}
return ans;
}
int dp[55][105][4] = {0};// dp[i][j][k] 表示 前 i 个元素 中选择 k 个元素 构成大小 j 的方案数
int countQuadruplets(vector<int>& nums) {
int n = nums.size();
int res = 0;
dp[0][0][0] = 1;
for(int i = 1;i <= n;i ++) {
int v = nums[i - 1];
dp[i][0][0] = 1;
for(int j = 1;j < 105;j ++) {
for(int k = 1;k < 4;k ++) {
dp[i][j][k] += dp[i - 1][j][k];
if(j - v >= 0 && k - 1 >= 0) dp[i][j][k] += dp[i - 1][j - v][k - 1];
}
}
}
for(int i = 3;i < n;i ++) {
res += dp[i][nums[i]][3];
}
return res;
}