数字

来源:互联网 发布:网络教育专升本难度 编辑:程序博客网 时间:2024/04/27 19:58

Description

给出一个整数n和一个数组a。数组a中包含n个整数,编号0到9。 
你的任务是统计满足下列条件的正整数的个数: 
    1.该正整数的长度不能超过n位; 
    2.该正整数的最高位不能是0; 
    3.数字i(0<=i<=9)在该正整数中至少出现a[i]次

Input

第一行一个整数n(1<=n<=100)。 
第二行,10个空格间隔的整数,表示a[0],a[1]...a[9]里面存的数字

Output

一行,一个整数,表示满足条件的数字的个数,结果可能很大,输出与1000000007取模后的结果。

Sample Input

样例输入1:10 0 0 0 0 0 0 0 0 1样例输入2:21 1 0 0 0 0 0 0 0 0样例输入3:31 1 0 0 0 0 0 0 0 0

Sample Output

样例输入1:1样例输入2:1样例输入3:36

Hint

样例说明: 
满足样例1的数字只有数字"9" 
满足样例2的数字只有数字"10" 

满足样例3的数字有"10,110,101,100,102,103,120,210,301,......"总共36个,这些数字都满足0出现了至少1次,1出现了至少1次,其他数字出现了至少0次,并且长度不超过3。


【分析】

        一拿到这道题,感觉是搜索,因为之前做过一道类似的,但事实证明这道题搜索只能过4个点。听了老师的讲解后才知道这原来是DP。具体如下:

        F[i][j]表示长度为i的由j,j+1,...,9组成的数字个数

       有了这个状态数组(实在是很非主流的一种)后,方程也就不难写了。关键是要思考F[i][j]可以由哪些状态继承过来。很明显:

        F[i][j]可以由F[i-k][j+1]继承(a[j]<=k<=i)。也就是说由长度为i-k的 还没出现j这个数字的 方案继承过来,即选择k个j放入数字中。那么得到DP方程:

       F[i][j]=sum{F[i-k][j+1]+c[i][k]};(a[j]<=i<=N;a[j]<=k<=i;1<=j<=9;)c[i][k]表示i个中选择k个的组合。

        之所以上式中的j不能等于0,是因为题目中的条件2。而在j等于0的时候的时候,只需要把组合数改为c[i-1][k]就可以了(因为最高位不能放0,所以可以有i-1个位置供选择)。

        现在再来看看这道题,其实我们是用DP来思考(定状态,找继承),可是却没有决策,这就变成了递推了。在实际做题的过程中,我们常常会发现,用DP的思维来做递推题往往事半功倍,很容易找出递推公式。所以DP思维是我们需要强化和训练的。


【代码】

#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<ctime>#include<iostream>#include<algorithm>using namespace std;const int MODER=1000000007;long long c[205][205];int N,a[10];long long f[205][15];void _get_c()                 //利用递推,预处理求出组合数组 {                             //c[i][j]=c[i-1][j]+c[i-1][j-1] c[0][0]=1;for(int i=1;i<=N;i++){c[i][0]=1;    for(int j=1;j<=i;j++)        c[i][j]=(c[i-1][j]+c[i-1][j-1])%MODER;}}void _init(){scanf("%d",&N);for(int i=0;i<=9;i++)    scanf("%d",&a[i]);}void _solve(){_get_c();for(int j=9;j>=0;j--)              //j逆向往前推 {if(j==9)                       //j==9的时候要赋初值 {for(int i=a[j];i<=N;i++)    f[i][j]=1;}else if(j==0)                   //j==0的时候递推关系变化 {                               //单独讨论 for(int i=a[j];i<=N;i++)    for(int k=a[j];k<=i;k++)             if(i>=1)            f[i][j]=(f[i][j]+f[i-k][j+1]*c[i-1][k])%MODER;}            //一个细节上的问题,这里要加上花括号,不然else会悬挂到这个if上面 else        {for(int i=a[j];i<=N;i++)    for(int k=a[j];k<=i;k++)        f[i][j]=(f[i][j]+f[i-k][j+1]*c[i][k])%MODER;}}int ans=0;for(int i=1;i<=N;i++)    ans=(ans+f[i][0])%MODER;          //求出答案 printf("%d\n",ans);}int main(){_init();_solve();return 0;}


原创粉丝点击