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

31 阅读3分钟

问题描述

小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

Solution

思考发现,这个问题是可以划分成子问题的。具体来说,可以只关注其中一组,每当选定一个数字,会有加入这一组和加入另一组两种选择。我们可以遍历数字和的最后一位,也就是 0-10 ,这样,这两种选择就会对应到与原问题相同的子问题,同时,将这两个子问题的方案数加起来,就能够得到原问题的答案,那么就可以通过动态规划来解决这个问题。

使用 dp[i][j] 表示选择到第i个数字,数字和最后一位为 j 的方案数,那么就能得到状态转移方程

dp[i][j]=(dp[i][j]+dp[i1][j]+dp[i1][(jarr[i]+10)dp[i][j] = (dp[i][j] + dp[i-1][j] + dp[i-1][(j-arr[i] + 10) % 10])

初始状态,不选择数字,数字和的最后一位是0是一种方案,所以 dp[0][0]=1

同时,数字的顺序在这里是无关紧要的,所以直接从前向后遍历即可

特殊情况判断

  • 如果所有数字划分到一组,另一组为空,也就是 sum==A||sum==B 这时候只会有一种方案。因为 A 或 B 是非零的(我记得有这个条件,但是貌似更新过程中更新掉了?也可能是记错题目了,但是这样的确能过,所以应该是非零的)如果划分一些数字到空的那一组,那么非空的这一组必然不会满足数字和的最后一位的限制
  • 如果两组都非空的话,首先可以判断 sum==(A+B)%10 这个条件,不满足的话不需要进行后面的计算,直接返回 0 即可
C++代码实现
 int solution(int n, int A, int B, std::vector<int> arr) {
     // Please write your code here
     int sum = 0;
     for(int i = 0; i < n; i++){
         arr[i] %= 10;
         sum += arr[i];
     }
     sum %= 10;
 ​
     if(sum == A || sum == B) return 1;
     if(sum != (A + B) % 10) return 0;
 ​
     vector<vector<int>> dp(n+1, vector<int>(10, 0));
     dp[0][0] = 1;
 ​
     for(int i = 1; i <= n; i++){
         for(int j = 0; j < 10; j++){
             dp[i][j] += dp[i-1][j];
             dp[i][j] += dp[i-1][(j - arr[i-1] + 10) % 10];
         }
     }
     return dp[n][A];
 }
时空复杂度

时间复杂度: O(n)

空间复杂度: O(n)

总结与思考

一开始比较容易的想到了动态规划,但是在怎么设计状态转移这里却卡住了。一开始想的是,使用 dp[i][A][B] 来表示选到第i个数字,一组的和为 A ,另一组的和为 B 的方案数,但是这样貌似是会导致重复计数的问题,但是想的不是很明白哪里重复,重复了多少。

最后问了一下 AI ,转变了思路,然后成功 AC ,不得不说在苦思不解的时候通过 AI 来打开思路还是可以的,现在面临 AI 时代,在学习中也可以多使用 AI 的帮助提升学习效率。