NC16576 [NOIP2012]摆花

题目链接

题目

题目描述

小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的n 种花,从1 到n 标号。为了在门口展出更多种花,规定第i 种花不能超过ai 盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。

试编程计算,一共有多少种不同的摆花方案。

输入描述

第一行包含两个正整数n和m,中间用一个空格隔开。
第二行有n个整数,每两个整数之间用一个空格隔开,依次表示a1、a2、……an。

输出描述

输出只有一行,一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对1000007取模的结果。

示例1

输入

2 4
3 2

输出

2

说明

有2种摆花的方案,分别是(1,1,1,2),(1,1,2,2)。括号里的1和2表示两种花,比如第一个方案是前三个位置摆第一种花,第四个位置摆第二种花。

备注

对于20%数据,有0<n≤8,0<m≤8,0≤ai≤8;

对于50%数据,有0<n≤20,0<m≤20,0≤ai≤20;

对于100%数据,有0<n≤100,0<m≤100,0≤ ai≤100。

题解

知识点:背包dp,计数dp。

可以看作一个多重背包,每种花是看作物品,花的盆数是物品数量。于是有转移方程:

dp[i][j]=sum[j]sum[ja[i]1],sum[n]=j=1ndp[i1][j]

表示考虑到第 i 盆花已经摆了 j 盆时的方案数是考虑到 i1 盆摆了 [ja[i],j] 盆的方案数的总和,可以用前缀和优化,普通的多重背包就不一定可以这样优化qwq。

可以滚动数组优化空间。

题外话

一个经典模型 x1+x2++xm=n,xi1xiZ+ ,每个整数位置和值不同就是方案不同,问有多少种方案。

考虑把整数当成多个不加区分的 1 合在一起的结果,用不加区分的隔板分隔不同的整数。例如:111|11|1111|1|1,就分出了 [3,2,4,1,1]

于是对于这个模型,有 m1 个隔板,n1 ,一共会有 n+m1 个位置放 1 和隔板,先放隔板有 Cn+m1m1 种方案,剩下的放 1Cnn 种方案,共计 Cn+m1m1=Cn+m1n 种方案。

注意 xi1 是必须的,如果 xiai 则可以通过 xiai+1 的变形使得符合条件。

但本题是 1xiai ,就不能用这个经典模型了。

其实经典模型是个完全背包,而本题则是多重背包。

时间复杂度 O(nm)

空间复杂度 O(m)

代码

#include <bits/stdc++.h>
using namespace std;
const int mod = 1000007;
int a[107], dp[107], sum[107];///第i种花,一共摆了j个
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
dp[0] = 1;
for (int i = 1;i <= n;i++) cin >> a[i];
for (int i = 1;i <= n;i++) {
sum[0] = dp[0];
for (int j = 1;j <= m;j++) sum[j] = (sum[j - 1] + dp[j]) % mod;
for (int j = 0;j <= m;j++)
dp[j] = (sum[j] - (j - a[i] - 1 < 0 ? 0 : sum[j - a[i] - 1]) + mod) % mod;
}
cout << dp[m] << '\n';
return 0;
}
posted @   空白菌  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示