一、题目描述
小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 m 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的 n 种花,从 1 到 n 标号。为了在门口展出更多种花,规定第 i 种花不能超过 ai 盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。
输入描述
第一行包含两个正整数 n 和 m,中间用一个空格隔开。
第二行有 n 个整数,每两个整数之间用一个空格隔开,依次表示 a1、a2、......an。
其中,0<n≤100,0<m≤100,0≤ai≤100。
输出描述
输出只有一行,一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案1e6+7取模的结果。
输入输出样例
示例
输入
2 4
3 2
输出
2
二、代码展示
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
int m=scan.nextInt();
int[][] dp=new int[n+1][m+1];//n为花的种类,m为总共盆数
int[] a=new int[n+1];//用来表示每种花摆多少盆
for(int i=1;i<=n;i++) {
a[i]=scan.nextInt();
}
for(int i=0;i<=n;i++) {//表示第i种花摆0盆为1种方案
dp[i][0]=1;
}
for(int i=1;i<=n;i++) {//遍历花的种类
for(int j=1;j<=m;j++) {//遍历花的总盆数
for(int k=0;k<=j&&k<=a[i];k++) {
dp[i][j]+=dp[i-1][j-k];//表示第i种花摆j盆的方案数
dp[i][j]%=(int)1e6+7;
}
}
}
System.out.println(dp[n][m]);
}
}
逐行解释
1. 外层循环:遍历花的种类
for (int i = 1; i <= n; i++) {
作用:遍历每一种花,从第
1
种到第n
种。意义:动态规划的阶段,逐步考虑前
i
种花的摆放方案。
2. 中层循环:遍历花的总盆数
for (int j = 1; j <= m; j++) {
作用:遍历需要摆放的花的总盆数,从
1
盆到m
盆。意义:动态规划的状态,计算前
i
种花摆j
盆的方案数。
3. 内层循环:遍历第
i
种花摆k
盆for (int k = 0; k <= j && k <= a[i]; k++) {
作用:遍历第
i
种花可以摆放的盆数k
,k
的取值范围是:
0
到j
(不能超过总盆数j
)。
0
到a[i]
(不能超过第i
种花的最大盆数)。意义:枚举第
i
种花摆放的盆数k
,并累加前i-1
种花摆j-k
盆的方案数。
4. 状态转移
dp[i][j] += dp[i - 1][j - k];
作用:更新
dp[i][j]
,表示前i
种花摆j
盆的方案数。意义:
dp[i][j]
是前i
种花摆j
盆的方案数。
dp[i - 1][j - k]
是前i-1
种花摆j - k
盆的方案数。通过累加所有可能的
k
,得到前i
种花摆j
盆的总方案数。
5. 取模防止溢出
dp[i][j] %= (int) 1e6 + 7;
作用:对
dp[i][j]
取模,防止数值过大导致溢出。意义:
1e6 + 7
是题目要求的模数(即1000007
)。每次更新
dp[i][j]
后,取模确保结果在合理范围内。
动态规划的状态转移方程
设
dp[i][j]
表示前i
种花摆j
盆的方案数。状态转移方程:
𝑑𝑝[𝑖][𝑗]=∑𝑘=0min(𝑗,𝑎[𝑖])𝑑𝑝[𝑖−1][𝑗−𝑘]dp[i][j]=k=0∑min(j,a[i])dp[i−1][j−k]
其中,
k
表示第i
种花摆k
盆。
dp[i-1][j-k]
表示前i-1
种花摆j-k
盆的方案数。
示例说明
假设:
n = 2
(2 种花)。
m = 4
(总共摆 4 盆花)。
a[1] = 3
(第 1 种花最多摆 3 盆)。
a[2] = 2
(第 2 种花最多摆 2 盆)。DP 表填充过程:
初始化:
dp[0][0] = 1
。其他
dp[i][0] = 1
。填充
i = 1
(第 1 种花):
j = 1
:
k = 0
:dp[1][1] += dp[0][1] = 0
。
k = 1
:dp[1][1] += dp[0][0] = 1
。结果:
dp[1][1] = 1
。
j = 2
:
k = 0
:dp[1][2] += dp[0][2] = 0
。
k = 1
:dp[1][2] += dp[0][1] = 0
。
k = 2
:dp[1][2] += dp[0][0] = 1
。结果:
dp[1][2] = 1
。类似地,
dp[1][3] = 1
,dp[1][4] = 0
。填充
i = 2
(第 2 种花):
j = 1
:
k = 0
:dp[2][1] += dp[1][1] = 1
。
k = 1
:dp[2][1] += dp[1][0] = 1
。结果:
dp[2][1] = 2
。
j = 2
:
k = 0
:dp[2][2] += dp[1][2] = 1
。
k = 1
:dp[2][2] += dp[1][1] = 1
。
k = 2
:dp[2][2] += dp[1][0] = 1
。结果:
dp[2][2] = 3
。类似地,
dp[2][3] = 3
,dp[2][4] = 2
。最终结果:
dp[2][4] = 2
,即共有 2 种摆花方案。