题目解析
小M手中有 nnn 张卡牌,每张卡牌的正反面分别写有数字 a[i]a[i]a[i] 和 b[i]b[i]b[i]。她希望选择每张卡牌的一面,使得向上的数字之和可以被 333 整除。我们需要计算满足条件的不同组合数,并对结果取模 109+710^9 + 7109+7。
这一问题本质上是一个 动态规划问题,核心在于如何高效计算所有可能的数字和模 333 的情况,并在此过程中进行状态转移。
解题思路
1. 状态定义
使用动态规划数组 dp[i][j] 表示:
- 前 iii 张卡牌考虑完后,所有向上数字之和对 333 取模等于 jjj 的方案数。
- 其中 j∈{0,1,2}j \in {0, 1, 2}j∈{0,1,2}。
我们最终关心的是 dp[n][0],即 nnn 张卡牌选完后,向上数字之和能被 333 整除的方案数。
2. 初始状态
在没有选任何卡牌时:
- dp[0][0]=1dp[0][0] = 1dp[0][0]=1:数字之和为 000 且满足整除条件的方案数为 1(空集)。
- dp[0][1]=dp[0][2]=0dp[0][1] = dp[0][2] = 0dp[0][1]=dp[0][2]=0:没有选卡牌时,数字之和模 333 为 111 或 222 的方案数为 0。
3. 状态转移
对于第 iii 张卡牌:
- 如果选择正面数字 a[i]a[i]a[i],当前的模值会发生变化:(j+a[i])%3(j + a[i]) % 3(j+a[i])%3。
- 如果选择反面数字 b[i]b[i]b[i],当前的模值会发生变化:(j+b[i])%3(j + b[i]) % 3(j+b[i])%3。
因此,状态转移方程为:
dp[i][(j+a[i])%3]+=dp[i−1][j]dp[i][(j + a[i]) % 3] += dp[i-1][j]dp[i][(j+a[i])%3]+=dp[i−1][j] dp[i][(j+b[i])%3]+=dp[i−1][j]dp[i][(j + b[i]) % 3] += dp[i-1][j]dp[i][(j+b[i])%3]+=dp[i−1][j]
需要注意的是,每次更新后对结果取模 109+710^9 + 7109+7。
4. 结果输出
所有卡牌选完后,满足整除条件的方案数为 dp[n][0]。
优化分析
空间优化:
- 注意到在状态转移时,当前状态仅依赖上一状态,因此可以用一维数组
dp滚动更新,减少空间复杂度。
实现代码
以下是完整的 Java 实现:
java
Copy code
public class Main {
public static int solution(int n, int[] a, int[] b) {
int MOD = 1000000007;
// 定义 dp 数组,初始化为 0
int[] dp = new int[3];
dp[0] = 1; // 初始状态
for (int i = 0; i < n; i++) {
int[] next = new int[3]; // 临时数组存储当前状态
for (int j = 0; j < 3; j++) {
next[(j + a[i]) % 3] = (next[(j + a[i]) % 3] + dp[j]) % MOD;
next[(j + b[i]) % 3] = (next[(j + b[i]) % 3] + dp[j]) % MOD;
}
dp = next; // 更新 dp 数组
}
return dp[0]; // 最终答案
}
public static void main(String[] args) {
System.out.println(solution(3, new int[]{1, 2, 3}, new int[]{2, 3, 2}) == 3);
System.out.println(solution(4, new int[]{3, 1, 2, 4}, new int[]{1, 2, 3, 1}) == 6);
System.out.println(solution(5, new int[]{1, 2, 3, 4, 5}, new int[]{1, 2, 3, 4, 5}) == 32);
}
}
示例解释
-
输入:
n=3,a=[1,2,3],b=[2,3,2]n = 3, a = [1, 2, 3], b = [2, 3, 2]n=3,a=[1,2,3],b=[2,3,2]
输出:3
说明:三种满足条件的组合是:- a[1],a[2],b[3]a[1], a[2], b[3]a[1],a[2],b[3]:1 + 2 + 2 = 5 %3=0% 3 = 0%3=0。
- b[1],a[2],a[3]b[1], a[2], a[3]b[1],a[2],a[3]:2 + 2 + 3 = 7 %3=0% 3 = 0%3=0。
- a[1],b[2],b[3]a[1], b[2], b[3]a[1],b[2],b[3]:1 + 3 + 2 = 6 %3=0% 3 = 0%3=0。
-
输入:
n=4,a=[3,1,2,4],b=[1,2,3,1]n = 4, a = [3, 1, 2, 4], b = [1, 2, 3, 1]n=4,a=[3,1,2,4],b=[1,2,3,1]
输出:6
时间与空间复杂度
- 时间复杂度:O(n×3)=O(n)O(n \times 3) = O(n)O(n×3)=O(n),每张卡牌最多更新 333 种模值。
- 空间复杂度:O(3)=O(1)O(3) = O(1)O(3)=O(1),使用滚动数组优化空间。
总结
本题通过动态规划解决了一个组合数统计问题。核心在于理解模 333 的状态转移,并通过空间优化实现高效计算。这种动态规划思路可扩展到更多模运算相关的场景。