题目来源:

http://129.211.20.246/problem.php?id=1028

题目描述:

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

输入

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

数据规模: 
对于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。 
 

输出

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

下面是对样例数据的解释: 
有2种摆花的方案,分别是(1,1,1,2),(1,1,2,2)。括号里的1和2表示两种花,比如第一个方案是前三个位置摆第一种花,第四个位置摆第二种花。 

样例输入 

2 4
3 2

样例输出 

2

来源/分类

2012年NOIP全国联赛普及组  

思路:

显而易见,这是一道dp题目。
状态转移方程式是什么呢?
dp[n][m]//表示在m号位置放共n种花,此时的方案总数。
=dp[n-1][m-1]+dp[n-1][m-2]+dp[n-1][m-3]+......+dp[n-1][0]
因为题目说:花要按顺序摆放,所以在m号位置放n种花的方案数就只需要看第n-1种花在几号位置停止就可以了。
初始化呢?我们只需把只放第0种花0位置的方案数改为1(这也是一种方案嘛)。
这样是还不够的:
状态转移方程式里,我们一直从dp[n-1][m-1]加到了dp[n-1][0],意图求上一种花在几号位置停下的方案总数,但是,
举个例子:共有3种花,6个位置。
郁金香编号为1,有2朵。
杜鹃花编号为2,有3朵。
百合花编号为3,有2朵。
那么dp[3][6]=dp[2][5]+dp[2][4]+dp[2][3]+dp[2][2]+dp[2][1]+dp[2][0]。
问题来了,可以加上dp[2][2]?
dp[2][2]表示在2号位置放杜鹃,3号位置就要开始摆百合。可是百合只有两朵!根本摆不下!(背包)
所以,循环的控制很重要。

参考代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#define N 1005
#define mod 1000007
using namespace std;
int a[N];
int dp[N][N];
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        memset(dp,0,sizeof(dp));
        for(int i=1; i<=n; i++)
            cin>>a[i];
        dp[0][0]=1;
        for(int i=1; i<=n; i++)
            for(int j=0; j<=m; j++)
                for(int k=0; k<=a[i]; k++)
                    dp[i][j]+=dp[i-1][j-k],dp[i][j]%=mod;
        cout<<dp[n][m]<<endl;
    }
    return 0;
}