【2024华为OD-E卷-100分-比赛】(题目+思路+Java&C++&Python解析)

题目描述

给定一个整数数组 nums 和一个目标分数 target,你需要从数组中选择一些元素(可以重复选择),使得这些元素的总和恰好等于目标分数 target。返回所有可能的组合(每个组合中的元素按非递减顺序排列),并且每个元素只能使用一次(即不允许重复选择同一个元素)。

输入

  • nums:一个整数数组,其中 1 <= nums.length <= 20,-100 <= nums[i] <= 100。
  • target:一个整数,-1000 <= target <= 1000。

输出

  • 所有满足条件的组合,每个组合中的元素按非递减顺序排列。

注意

  • 结果中的每个组合中的元素不能重复。
  • 返回的结果需要按组合中元素的非递减顺序排列,且结果中的组合也需要按非递减顺序排列。

思路

这个问题可以通过回溯法来解决。回溯法是一种通过递归和剪枝来搜索所有可能解的方法。具体步骤如下:

  1. 排序:首先将数组进行排序,这样可以方便后续的组合生成和剪枝。
  2. 回溯函数:定义一个回溯函数,该函数接收当前组合、当前索引和目标值作为参数。
  3. 递归和剪枝
    • 在每一步,可以选择当前索引的元素加入组合,并更新目标值。
    • 如果目标值变为0,表示找到了一个满足条件的组合,将其加入结果集。
    • 如果目标值小于0,或者当前索引已经遍历完,则回溯。
    • 否则,继续递归调用回溯函数,但索引需要加1,以避免使用重复元素。
  4. 结果输出:最终返回所有满足条件的组合。

Java 代码解析

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CombinationSum {
    public List<List<Integer>> combinationSum2(int[] nums, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        backtrack(result, new ArrayList<>(), nums, target, 0);
        return result;
    }

    private void backtrack(List<List<Integer>> result, List<Integer> tempList, int[] nums, int remain, int start) {
        if (remain == 0) {
            result.add(new ArrayList<>(tempList));
            return;
        }
        if (remain < 0 || start >= nums.length) {
            return;
        }
        for (int i = start; i < nums.length; i++) {
            // 避免使用重复元素
            if (i > start && nums[i] == nums[i - 1]) {
                continue;
            }
            tempList.add(nums[i]);
            backtrack(result, tempList, nums, remain - nums[i], i + 1);
            tempList.remove(tempList.size() - 1);
        }
    }

    public static void main(String[] args) {
        CombinationSum cs = new CombinationSum();
        int[] nums = {10, 1, 2, 7, 6, 1, 5};
        int target = 8;
        System.out.println(cs.combinationSum2(nums, target));
    }
}

C++ 代码解析

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    vector<vector<int>> combinationSum2(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        backtrack(result, vector<int>(), nums, target, 0);
        return result;
    }

private:
    void backtrack(vector<vector<int>>& result, vector<int>& tempList, const vector<int>& nums, int remain, int start) {
        if (remain == 0) {
            result.push_back(tempList);
            return;
        }
        if (remain < 0 || start >= nums.size()) {
            return;
        }
        for (int i = start; i < nums.size(); ++i) {
            // 避免使用重复元素
            if (i > start && nums[i] == nums[i - 1]) {
                continue;
            }
            tempList.push_back(nums[i]);
            backtrack(result, tempList, nums, remain - nums[i], i + 1);
            tempList.pop_back();
        }
    }
};

int main() {
    Solution solution;
    vector<int> nums = {10, 1, 2, 7, 6, 1, 5};
    int target = 8;
    vector<vector<int>> result = solution.combinationSum2(nums, target);
    for (const auto& combination : result) {
        for (int num : combination) {
            cout << num << " ";
        }
        cout << endl;
    }
    return 0;
}

Python 代码解析

def combinationSum2(nums, target):
    def backtrack(result, tempList, remain, start):
        if remain == 0:
            result.append(tempList[:])
            return
        if remain < 0 or start >= len(nums):
            return
        for i in range(start, len(nums)):
            # 避免使用重复元素
            if i > start and nums[i] == nums[i - 1]:
                continue
            tempList.append(nums[i])
            backtrack(result, tempList, remain - nums[i], i + 1)
            tempList.pop()
    
    result = []
    nums.sort()
    backtrack(result, [], target, 0)
    return result

# 测试
nums = [10, 1, 2, 7, 6, 1, 5]
target = 8
print(combinationSum2(nums, target))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

执着的小火车

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值