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

154 阅读4分钟

问题描述

小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

Tips:数组里的数字可以相等,但每个数字都是独一无二的。

比如 array_a = [1, 1, 1],A = 1,B = 2,则一共有三组划分方式:
第一种: A:a[0] B:a[1]、a[2]
第二种: A:a[1] B:a[0]、a[2]
第三种: A:a[2] B:a[0]、a[1]

样例2:

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

Tips:可以将所有数字都划分到同一组,使其和的个位数等于 A 或 B;另一组为空。

比如 array_a = [1, 1, 1],A = 3,B = 5,则共有一组划分方式:
A:a[0]、a[1]、a[2] B:空

样例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.规范化数组:

将数组中的每个元素取模 10,确保所有元素都在 0 到 9 的范围内。这样做是因为我们只关心和的个位数。

2.计算总和的个位数:

计算整个数组的和的个位数 total_sum。如果这个和的个位数等于 A 或 B,那么我们可以直接返回 1,因为此时可以将一组设置为空,另一组的和即为 total_sum

3.检查总和的条件:

如果 total_sum 不等于 (A + B) % 10,那么就没有可能的划分,因为两个组的和的个位数之和不可能等于总和的个位数。

4.动态规划:

1)使用动态规划的方法来计算可能的划分方式。我们定义一个二维数组 f[i][j],表示前 i 个数字中,能够组成和的个位数为 j 的方式数量。

2)初始化 f[0][0] = 1,表示使用 0 个数字可以得到和为 0 的一种方式。

3)对于每个数字,我们可以选择将其放入第一组或不放入第一组,从而更新状态。

5.最终结果:

最终的结果是 f[n][B],表示使用所有数字能够得到和的个位数为 B 的方式数量。

代码实现(CPP)

#include <iostream>
#include <vector>
#include <numeric>

using namespace std;

int solution(int n, int A, int B, vector<int>& array_a) {
    // 将元素规范化到 [0, 9] 的范围内
    for (int& x : array_a) {
        x %= 10;
    }
    
    int total_sum = accumulate(array_a.begin(), array_a.end(), 0) % 10;
    
    // 检查总和是否符合要求
    if (total_sum == A || total_sum == B) {
        return 1;
    }
    if (total_sum != (A + B) % 10) {
        return 0;
    }

    // 动态规划数组初始化
    vector<vector<int>> f(n + 1, vector<int>(10, 0));
    f[0][0] = 1;

    // 动态规划更新
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < 10; ++j) {
            f[i][j] += f[i - 1][j]; // 不将第 i 个数字放入第一组
            f[i][j] += f[i - 1][(j - array_a[i - 1] + 10) % 10]; // 将第 i 个数字放入第一组
        }
    }

    return f[n][B];
}

int main() {
    //  You can add more test cases here
    std::vector<int> array1 = {1, 1, 1};
    std::vector<int> array2 = {1, 1, 1};
    std::vector<int> array3 = {1, 1};

    std::cout << (solution(3, 1, 2, array1) == 3) << std::endl;
    std::cout << (solution(3, 3, 5, array2) == 1) << std::endl;
    std::cout << (solution(2, 1, 1, array3) == 2) << std::endl;

    return 0;
}

复杂度分析

时间复杂度

动态规划的状态转移需要 O(n * 10),其中 n 是数组的大小,10 是个位数的范围。因此,整体的时间复杂度为 O(n)。

空间复杂度

我们使用了一个二维数组 f,其大小为 (n + 1) * 10,因此空间复杂度为 O(n)。