P1077 [NOIP 2012 普及组] 摆花
题目大意
小明要在花店门口摆放 m 盆花,共有 n 种不同的花,第 i 种花最多摆放 a[i] 盆。要求:
-
同一种花必须摆放在一起;
-
不同种类的花必须按编号顺序摆放(即编号小的花在前,编号大的花在后)。
求所有可能的摆花方案数,结果对 1e6 + 7 取模。
解题思路
本题属于动态规划(DP)中的多重背包问题,我们可以用 dp[j] 表示摆放 j 盆花的方案数。
状态定义
-
dp[j]:当前已经考虑了前i种花,摆放j盆花的方案数。
状态转移
-
初始化:
-
dp[0] = 1,表示摆放0盆花只有1种方案(什么都不摆)。
-
-
遍历每种花
i:-
从后往前遍历
j(防止重复计算)。 -
对于每种花
i,可以摆放k盆(1 ≤ k ≤ min(j, a[i]))。 -
更新
dp[j]:-
dp[j] += dp[j - k](即当前摆放k盆花,剩余j - k盆由前i-1种花摆放)。
-
-
最终答案
-
dp[m]即为摆放m盆花的所有方案数。
代码解析
#include<bits/stdc++.h> using namespace std; const int N = 1e2 + 10, mod = 1e6 + 7; int n, m, a[N]; int dp[N]; // dp[j] 表示摆放 j 盆花的方案数 int main() { cin >> n >> m; for(int i = 1; i <= n; i++) cin >> a[i]; dp[0] = 1; // 初始化:0 盆花只有 1 种方案(不摆) for(int i = 1; i <= n; i++) // 遍历每种花 for(int j = m; j >= 0; j--) // 从后往前遍历,防止重复计算 for(int k = 1; k <= min(j, a[i]); k++) // 当前花可以摆 k 盆 dp[j] = (dp[j] + dp[j - k]) % mod; // 状态转移 cout << dp[m]; // 输出摆放 m 盆花的方案数 return 0; }
关键点
-
dp[0] = 1:-
表示不摆放任何花也是一种方案。
-
-
倒序遍历
j:-
避免重复计算(类似背包问题的优化)。
-
-
k的限制:-
k ≤ min(j, a[i])保证不超过剩余花盆数和该种花的限制。
-
-
取模运算:
-
由于方案数可能很大,每次更新
dp[j]时都要取模1e6 + 7。
-
复杂度分析
-
时间复杂度:
O(n × m × max(a[i])),本题数据范围n, m ≤ 100,完全可接受。 -
空间复杂度:
O(m),仅用一维数组优化。
总结
本题是经典的多重背包问题,通过动态规划递推求解,关键点在于:
-
状态定义:
dp[j]表示摆放j盆花的方案数。 -
状态转移:考虑每种花摆放
k盆的情况。 -
优化:倒序遍历
j避免重复计算。
适用于动态规划入门练习,也可作为背包问题的变种加深理解。

浙公网安备 33010602011771号