二分数组合|豆包MarsCode AI刷题

73 阅读4分钟

二分数组合|豆包MarsCode AI刷题

问题描述

小F面临一个有趣的挑战:给定一个数组,她需要将数组中的数字分为两组。分组的目标是使得一组数字的和的个位数等于给定的 A,另一组数字的和的个位数等于给定的 B。除此之外,还有一种特殊情况允许其中一组为空,但剩余数字和的个位数必须等于 A 或 B。小F需要计算所有可能的划分方式。

例如,对于数组 [1, 1, 1] 和目标 A = 1,B = 2,可行的划分包括三种:每个 1 单独作为一组,其余两个 1 形成另一组。如果 A = 3,B = 5,当所有数字加和的个位数为 3 或 5 时,可以有一组为非空,另一组为空。


测试样例

样例1:

输入:n = 3,A = 1,B = 2,array_a = [1, 1, 1]
输出:3

样例2:

输入:n = 3,A = 3,B = 5,array_a = [1, 1, 1]
输出:1

样例3:

输入:n = 2,A = 1,B = 1,array_a = [1, 1]
输出:2

样例4:

输入:n = 5,A = 3,B = 7,array_a = [2, 3, 5, 7, 9]
输出:0

问题分析

  1. 目标

    • 将数组中的数字分为两组,使得一组数字的和的个位数等于 A,另一组数字的和的个位数等于 B。
    • 允许其中一组为空,但非空组的和的个位数必须等于 A 或 B。
  2. 特殊情况

    • 如果所有数字的和的个位数既不是 A 也不是 B,则不可能存在满足条件的分组。
    • 如果存在一组为空,则另一组的和的个位数必须等于 A 或 B。
  3. 计算和的个位数

    • 对于任意数字 x,其个位数可以通过 x % 10 计算得到。
    • 数组的和的个位数可以通过遍历数组并累加每个数字的个位数,然后对 10 取模得到。
  4. 动态规划思路

    • 使用一个二维数组 dp,其中 dp[i][j] 表示前 i 个数字能否组合成和的个位数为 j
    • 初始化 dp[0][0] = True,表示空集合的和的个位数为 0。
    • 遍历数组,对于每个数字 num,更新 dp 数组,考虑将其加入当前组合或不加入当前组合两种情况。

我的代码

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

public class Main {
    // 计算所有子序列的和
    public static List<Integer> findAllSubsequenceSums(int[] array_a) {
        List<Integer> sums = new ArrayList<>();
        backtrack(array_a, 0, 0, 0, sums); // 添加一个参数表示当前子序列的大小
        return sums;
    }

    // 回溯法生成所有子序列和
    private static void backtrack(int[] array_a, int index, int currentSum, int size, List<Integer> sums) {
        // 只在子序列非空时添加和
        if (size > 0) {
            sums.add(currentSum);
        }
        
        // 遍历每个元素,生成子序列
        for (int i = index; i < array_a.length; i++) {
            currentSum += array_a[i]; // 选择当前元素
            backtrack(array_a, i + 1, currentSum, size + 1, sums); // 递归调用
            currentSum -= array_a[i]; // 撤销选择
        }
    }

    // 主要解决方案
    public static int solution(int n, int A, int B, int[] array_a) {
        int totalSum = 0;
        for (int num : array_a) {
            totalSum += num; // 计算总和
        }

        List<Integer> sums = findAllSubsequenceSums(array_a);
        int count = 0;

        // 遍历所有子序列和
        for (int sum : sums) {
            // 排除空子序列和整个原始序列的情况
            if (sum % 10 == A && sum != totalSum) {
                // 检查总和减去当前子序列和的个位数
                if ((totalSum - sum) % 10 == B) {
                    count++; // 计数符合条件的分组
                }
            }
        }

        // 检查总和的个位数是否等于 A 或 B
        if (totalSum % 10 == A || totalSum % 10 == B) {
            count++; // 计数特殊情况
        }

        return count; // 返回结果
    }

    public static void main(String[] args) {
        // 测试用例
        int[] array1 = {1, 1, 1};
        int[] array2 = {1, 1, 1};
        int[] array3 = {1, 1};

        // 输出验证
        System.out.println(solution(3, 1, 2, array1) == 3);
        System.out.println(solution(3, 3, 5, array2) == 1);
        System.out.println(solution(2, 1, 1, array3) == 2);
    }
}

119.png

我的代码思路

  1. 计算总和:代码首先遍历数组,计算出所有元素的总和。
  2. 生成子序列和:通过回溯法,代码生成了数组的所有可能子序列的和,并将其存储在sums列表中。这里有个关键细节需要注意:为了排除空子序列(其和为0),只有当子序列非空时(即size > 0),才将当前和添加到sums中。
  3. 检查分组条件:对于sums中的每个和,代码检查其个位数是否与A相等,并且这个和不能等于数组的总和(以排除整个数组作为一个组的情况)。然后,它检查数组总和减去当前子序列和的个位数是否与B相等。如果这两个条件都满足,则计数增加。
  4. 处理特殊情况:代码还考虑了数组总和的个位数直接等于A或B的特殊情况,此时也增加计数。