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

137 阅读5分钟

问题描述

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

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

小F面临的这个问题可以归纳为一个组合数学问题,重点是将一个数组的元素分成两个子集,使得这两个子集的和的最后一位数字分别等于给定的 A 和 B(或其中一个子集可以为空)。

分步解析

  1. 和的个位数理解

    • 给定两个整数 A 和 B,它们的个位数分别是需要我们关注的。
    • 这样的分法最终依赖于所有数字的和模 10 的结果。
  2. 特殊情况

    • 允许一组为空,这意味着只要剩余的一组的和的个位数符合 A 或 B,划分仍然是有效的。
  3. 例子

    • 假设数组为 [1, 1, 1],A = 1,B = 2:
      • 可能的划分方式可以是:
        1. 第一组:[1] -> 和的个位数 = 1,第二组:[1, 1] -> 和的个位数 = 2
        2. 第一组:[1] -> 和的个位数 = 1,第二组:[1] -> 和的个位数 = 1(这不匹配 B)
        3. 第一组:[1, 1] -> 和的个位数 = 2,第二组:[1] -> 和的个位数 = 1
        4. 只选择空组,和的个位数取决于非空组(比如:[1, 1, 1] 的总和 = 3,个位数 = 3)、但和的个位数不满足 A 或 B,则不计算在内。
  4. 动态规划方法

    • 一个可能的解决方案是使用动态规划来计算不同的和。
    • dp[i][j] 表示前 i 个数字中,和的个位数为 j 的可能组合数。
    • 状态转移方程可以基于当前数字选择与否来更新已有的组合。
  5. 最终计算

    • 完成动态规划后,需要检查所有在 A、B 上的组合方式,统计有效划分的总数。

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

public class Main { // 计算所有子序列的和 public static List findAllSubsequenceSums(int[] array_a) { List 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);
}

}

这段Java代码的主要功能是计算一个整数数组的所有非空子序列的和,并根据特定条件对这些和进行计数。下面是代码的详细解析:

  1. 导入必要的类:代码开始时导入了ArrayListList类,以便使用动态数组来存储子序列的和。

  2. findAllSubsequenceSums 方法

    • 输入:一个整数数组 array_a
    • 输出:一个包含所有子序列和的列表。
    • 该方法调用 backtrack 方法来生成所有子序列和。
  3. backtrack 方法

    • 输入参数包括数组、当前索引、当前子序列的和、当前子序列的大小和存储子序列和的列表。
    • 方法首先检查当前子序列的大小,如果大于零,则将当前和添加到结果列表中。
    • 然后,使用循环遍历数组中的元素,递归生成子序列:
      • 选择当前元素,更新当前和,并递归调用 backtrack 处理下一个元素。
      • 递归调用后,撤销选择,恢复当前和以便处理下一个元素。
  4. solution 方法

    • 输入:整数 n(数组长度),两个整数 AB,以及整数数组 array_a
    • 该方法首先计算数组的总和 totalSum
    • 然后调用 findAllSubsequenceSums 获取所有子序列和。
    • 接下来,遍历所有的子序列和,检查每个和是否满足条件:
      • 和的个位数是否等于 A,并且和不等于 totalSum
      • 如果满足上述条件,再检查 totalSum 减去当前和的个位数是否等于 B
      • 如果条件满足,计数器 count 增加。
    • 最后,检查 totalSum 的个位数是否等于 AB,如果是,则计数器 count 再增加。
  5. main 方法

    • 提供了一些测试用例来验证 solution 方法的正确性。
    • 使用 System.out.println 输出每个测试用例的结果,检查其是否与预期相符。

总结

这段代码通过回溯法生成所有非空子序列的和,并根据特定条件(子序列和的个位数与给定值 AB 的关系)进行计数。它展示了如何使用递归和回溯来解决组合问题,同时也强调了处理边界条件的重要性。