【问题描述】
小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 m 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的n 种花,从1 到n 标号。为了在门口展出更多种花,规定第i 种花不能超过a i 盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。
【输入格式】
第一行包含两个正整数 n 和 m,中间用一个空格隔开。
第二行有 n 个整数,每两个整数之间用一个空格隔开,依次表示 a1、a 2、…、a n 。
【输出格式】
输出只有一行,一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对 1000007 取模的结果。
【输入样例】
2 4
3 2
【输出样例】
2
【样例解释】
有2种摆花的方案,分别是(1,1 ,1 ,2),(1,1 ,2 ,2)。括号里的1和2表示两种花,比如第一个方案是前三个位置摆第一种花,第四个位置摆第二种花。
【数据范围】
对于 20%数据,有 n ≤8 ,m ≤8 ,ai≤8 ;
对于 50%数据,有 n≤20,m ≤20,ai≤20;
对于 100% 数据,有 n≤100,m ≤100,ai≤100 。
【思路梳理】
dp水题,非常简单的动态规划(只要你,呃,按照我们导师说的,入了门),先设状态函数d[i][j]=用前i种花中的j盆依次摆放所能够形成的不同的方案类型。对于正在填的第i行来说,所需要的每一个数据都来自于上面一行也就是第i-1行,所以可以使用二维/一维的滚动数组来实现。主意,如果使用一维滚动数组的话是要从右往左填的。
状态转移的过程:正在考虑第i种花时,可以由已经考虑过的第i-1种花转移过来,那么第i种花可以选择limit[i]朵来摆放,也可以选择limit[j]-1朵来摆放,当然,你也可以选择0朵来摆放。所以当我们在考虑d[i][j]时,根据的是d[i-1][j],d[i-1][j-1],etc转移过来的。需要注意到的是,可能不会有d[i-1][j-limit[i]]的状态(Limit[i]可能大于j)所以我们可以得到状态转移方程(大家凑合着看理解吧=_+):
时间复杂度:O(n*m*limit),虽然说有三重循环,但极限情况下仍然非常轻松可以通过(因为数据范围不会很大)。亲测后发现:使用二维滚动数组的速度明显快于使用普通二维数组。
【Cpp代码】
#include<cstdio>
#include<cstring>
#define maxn 105
using namespace std;
int n,m,limit[maxn],d[2][maxn];
//d[i][j]:从前i种花中选择总共j盆来摆放可以形成的方案总数
//变为二维滚动数组
int mod(int x)
{
return x%1000007;
}
void solve()
{
memset(d,0,sizeof(d));
d[0][0]=d[1][0]=1;//d[i][0]=1,不摆就是一种方案
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int t=0;
for(int k=0;k<=limit[i] && k<=j;k++)
t=mod(t+d[(i-1)%2][j-k]);
d[i%2][j]=mod(t);
}
printf("%d\n",mod(d[n%2][m]));
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&limit[i]);
solve();
return 0;
}