每日一题 数列计数(dp)

问题描述:
小初喜欢数列,其中特别喜欢 m 数列
所谓 m 数列,就是数列中所有数字和为 m 的数列.
小初热衷于计算 m 数列的个数,但是没有限制的 m 数列的个数是无穷无尽的.
因此小初会再加上一些限制:他希望这个 m 数列的长度为 n ,且数列第 i 个数应该是一个不超过 ai 的非负整数。
小初希望你也能感受到 m 数列的魅力,因此他想你数一数有这样限制的 m 数列有几个。
虽然有许多限制,但是这样的 m 数列可能仍然有很多,因此你只需要求出符合条件的 m 数列个数对 1000007 取模的结果。
输入描述:
第一行包含两个正整数 n 和 m ,中间用一个空格隔开。
第二行有 n 个整数,每两个整数之间用一个空格隔开,依次表示 a1,a2,a3…an-1,an
0 < n <= 100,0 < m <= 100,0 <= ai <= 100
输入描述:
输出格式
一个整数,表示有多少个 数列。注意:因为个数可能很多,请输出个数对
取模的结果。

示例:
输入 #1

2 4
3 2

输出 #1

2


输入 #2

3 5
1 2 3

输出 #2

3


思路:这种题我一般将其看作 m 个球放 n 个桶的球桶组合问题,但是这里有个 ai 的限制条件和允许空桶,需要稍加转换.

dp(i,j)表示 j 个球放 i 个桶的放法.
当前 i 桶可以不放球,放1个球,放两个球…最多放 ai 个球.
那么 i-1 个桶中,就是放 j 个球, j-1 个球, j-2 个球, j-ai 个球.

dp(i,j) = dp(i-1,j) + dp(i-1,j-1) + dp(i-1,j-2) +…+ dp(i-1)(j-ai).

再优化,由上式可得

dp(i,j-1) = dp(i-1,j-1) + dp(i-1,j-2) + … + dp(i-1,j-ai) + dp(i-1,j-1-ai).

合并

dp(i,j) = dp(i-1,j) +dp(i,j-1) - dp(i-1,j-1-ai).

至此,状态转移方程出来了.

#include <algorithm>
#include <cstdio>
using namespace std;
const int mod = 1e6 + 7;
int dp[105][105], a[105];
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
    }
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            //主要是dp(0,0)要为1
            //没有球放,不放球一种方法
            if (j == 0) {
                dp[i][j] = 1;
                continue;
            }
            //没有桶
            if (i == 0) {
                dp[i][j] = 0;
                continue;
            }
            //防止越界,当前只有3个球,ai为5
            //显然最后也只有3个球可以放,其实此时ai就是3.
            int tmp = min(j, a[i]);
            dp[i][j] = (dp[i - 1][j] + dp[i][j - 1]) % mod;
            //j-tmp必然>=0
            if (j - tmp - 1 >= 0) {
                dp[i][j] = (dp[i][j] - dp[i - 1][j - tmp - 1] + mod) % mod;
            }
        }
    }
    printf("%d", dp[n][m]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值