Round #131 (Div. 1) B. Numbers——dp

题意:给出长度为10的数组,规定了0~9这10个数位至少出现的次数,问对于给定的长度n,符合条件且长度不超过n同时没有前导0的数有多少个。

用最原始的思路:组合数学的方法,会发现有太多重复的情况会发生,而且几乎不可能不重复不遗漏的把所有的情况数完。一般这个时候就是动态规划派上用场的时候,既然我们不能一次把所有的数都加进来,但是我们可以一个一个的加:先算出满足9的目标数字的个数,再根据已得的数据算出同时满足8,9要求的目标数字的个数,以此类推。。。

dp[len][i]:表示长度为len,且满足i~9所有个数要求的目标数字的个数,从9开始递推,到0的时候注意一下前导0的处理,也就是组合数变一下。

#include <iostream>
#include <cstdio>
#include <cstring>
#define lng long long
using namespace std;

const int mod = 1000000007;
int num[11], n;
lng dp[105][11], c[105][105];

int main()
{
    freopen("in", "r", stdin);
    c[0][0] = 1;
    for(int i = 1; i < 105; ++i)
    {
        c[i][0] = c[i][i] = 1;
        for(int j = 1; j < i; ++j)
            c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
    }
    scanf("%d", &n);
    for(int i = 0; i < 10; ++i) scanf("%d", num + i);
    for(int i = 0; i <= n; ++i)
    {
        if(i < num[9]) dp[i][9] = 0;
        else dp[i][9] = 1;
    }
    for(int i = 8; i > 0; --i)
    {
        for(int j = 0; j <= n; ++j)
        {
            if(j >= num[i])
            {
                for(int k = 0; k <= j - num[i]; ++k)
                {
                    dp[j][i] += (dp[k][i + 1] * c[j][k]) % mod;
                    dp[j][i] %= mod;
                }
                //printf("%d %d %d\n", j, i, dp[j][i]);
            }
            else dp[j][i] = 0;
        }
    }
    for(int i = 0; i <= n; ++i)
    {
        if(i > num[0])
        {
            for(int j = 1; j <= i - num[0]; ++j)
            {
                dp[i][0] += (dp[j][1] * (c[i][j] - c[i - 1][j])) % mod;
                dp[i][0] %= mod;
                if(dp[i][0] < 0) dp[i][0] += mod;
            }
            //printf("%d %d %d\n", i, 0, dp[i][0]);
        }
        else dp[i][0] = 0;
    }
    lng res = 0;
    for(int i = 1; i <= n; ++i)
    {
        res += dp[i][0];
        res %= mod;
    }
    cout << res << "\n";
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值