P1077 [NOIP 2012 普及组] 摆花

题目大意

小明要在花店门口摆放 m 盆花,共有 n 种不同的花,第 i 种花最多摆放 a[i] 盆。要求:

  1. 同一种花必须摆放在一起;

  2. 不同种类的花必须按编号顺序摆放(即编号小的花在前,编号大的花在后)。

求所有可能的摆花方案数,结果对 1e6 + 7 取模。


解题思路

本题属于动态规划(DP)中的多重背包问题,我们可以用 dp[j] 表示摆放 j 盆花的方案数。

状态定义

  • dp[j]:当前已经考虑了前 i 种花,摆放 j 盆花的方案数。

状态转移

  1. 初始化:

    • dp[0] = 1,表示摆放 0 盆花只有 1 种方案(什么都不摆)。

  2. 遍历每种花 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;
}

关键点

  1. dp[0] = 1

    • 表示不摆放任何花也是一种方案。

  2. 倒序遍历 j

    • 避免重复计算(类似背包问题的优化)。

  3. k 的限制:

    • k ≤ min(j, a[i]) 保证不超过剩余花盆数和该种花的限制。

  4. 取模运算:

    • 由于方案数可能很大,每次更新 dp[j] 时都要取模 1e6 + 7


复杂度分析

  • 时间复杂度:O(n × m × max(a[i])),本题数据范围 n, m ≤ 100,完全可接受。

  • 空间复杂度:O(m),仅用一维数组优化。


总结

本题是经典的多重背包问题,通过动态规划递推求解,关键点在于:

  1. 状态定义:dp[j] 表示摆放 j 盆花的方案数。

  2. 状态转移:考虑每种花摆放 k 盆的情况。

  3. 优化:倒序遍历 j 避免重复计算。

适用于动态规划入门练习,也可作为背包问题的变种加深理解。

posted @ 2025-06-16 13:41  CRt0729  阅读(22)  评论(0)    收藏  举报