及格的组合方式探索
问题描述
问题描述
小S在学校选择了3门必修课和n门选修课程来响应全面发展的教育政策。现在期末考核即将到来,小S想知道他所有课程的成绩有多少种组合方式能使他及格。及格的条件是所有课程的平均分不低于60分。每门课程的成绩是由20道选择题决定,每题5分,答对得分,答错不得分。为了计算方便,你需要将结果对202220222022取模。
测试样例
样例1:
输入:
n = 3
输出:'19195617'
样例2:
输入:
n = 6
输出:'135464411082'
样例3:
输入:
n = 49
输出:'174899025576'
样例4:
输入:
n = 201
输出:'34269227409'
样例5:
输入:
n = 888
输出:'194187156114'
解题思路
在解决这种成绩组合问题时,初步想到的可能是使用递归方法,不断遍历每一种得分组合。递归的思想简单明了,每一门课程逐步累加分数,直到计算出所有课程的总分。然而,随着课程数量 ( n ) 的增大,递归的深度也会急剧增加,导致计算复杂度成倍提升。最终,这种递归方法往往会因为递归深度过高而导致栈溢出,或者因庞大的计算量而引发时间超时问题。
即使尝试使用记忆化(备忘录)来优化递归,以减少重复计算,效果也非常有限。记忆化只能在一定程度上避免重复计算,但对于大规模问题而言,递归的固有劣势仍然存在,难以根本性地解决时间和空间上的挑战。
为了解决这个问题,动态规划(DP)是一种更为高效的方案。动态规划通过建立一个状态转移表,分阶段存储每一门课程分数的所有组合情况,避免了深度递归带来的开销。相比递归,动态规划通过自底向上的方式逐步求解,能够有效控制时间复杂度,并且没有栈溢出的问题。
-
初始化:首先计算一门课程的所有得分组合。对于
dp[1][i],其中i是0到100的5的倍数,初始化为1,表示每种单门课程得分的组合方式。 -
状态转移:
- 从第二门课程开始迭代至第
n+3门课程(共n+3门课程)。 - 对于每门课程总得分
j,在前一门课程的所有可能分数基础上迭代(从0到100,每次步进5)。 - 如果
j >= k,则dp[i][j] += dp[i-1][j-k]表示第i门课程总得分j的组合数等于第i-1门课程的所有符合条件的组合数之和。 - 每次累加时对结果取模,以防止数值过大。
- 从第二门课程开始迭代至第
-
结果累加:
- 由于平均分不低于60分的条件,所有课程总分应不小于
60 * (n+3)。 - 遍历
dp[courseNumber][sumScore],即dp[n+3][sumScore],将符合条件的sumScore值累加,得到满足条件的所有组合数。
- 由于平均分不低于60分的条件,所有课程总分应不小于
代码
public class Main {
private static final long MOD = 202220222022L;
public static String solution(int n) {
int courseNumber = n + 3;
long[][] dp = new long[courseNumber+1][courseNumber * 100+1];
for(int i = 0; i <= 100; i+=5){
dp[1][i] = 1;
}
for(int i = 2; i <= courseNumber; i++){
for(int j = 0; j <= courseNumber * 100; j+=5){
for(int k = 0; k <= 100; k+=5){
if(j >= k){
dp[i][j] += dp[i-1][j - k];
dp[i][j] %= MOD;
}
}
}
}
long result = 0;
for (int sumScore = 60 * courseNumber; sumScore <= courseNumber * 100; sumScore+=5) {
result += dp[courseNumber][sumScore];
result %= MOD; // 取模
}
return Long.toString(result);
}
public static void main(String[] args) {
// You can add more test cases here
System.out.println(solution(3).equals("19195617"));
System.out.println(solution(6).equals("135464411082"));
System.out.println(solution(49).equals("174899025576"));
System.out.println(solution(201).equals("34269227409"));
System.out.println(solution(888).equals("194187156114"));
}
}