题目链接:http://acm.uestc.edu.cn/#/problem/show/1692
题意:已知n个参赛队员,对于第i个队员,每写一行代码,就会留下 ai个bug

最后一题需要写m行代码,请安排各个队员写的代码行数(显然要非负),使得整个代码的bug数不超过b个

然后,在ACM玄学之神的保佑下,这份不超过b个bug的代码就能AC了!

问你有多少种不同的安排方案可以写出一份AC代码,要求方案数对mod取模

解法:
dp[i][j][k],已经考虑了前i个队员,写了j行代码,存在k个Bug的方案数,通过递推顺序,第一维可以直接省略

if (k>=arr[i]) dp[j][k]=(dp[j][k]+dp[j-1][k-arr[i]])%mod; //这行代码的实际含义是dp[i][j][k]=(dp[i-1][j][k]+dp[i][j-1][k-arr[i]]),思考清楚为什么是这样的,尤其是为什么等式右边先是i-1,后面是i?
//转移方程似乎没有体现出一个队员可以写多份代码?提醒:这里的递推顺序实际上是一个无穷背包的思想

#include <bits/stdc++.h>
using namespace std;
const int maxn = 510;
typedef long long LL;
int n, m, b, a[maxn];
LL dp[maxn][maxn], mod;
//dp[i][j][k]表示前i个队员,写了j行代码,存在k个bug的方案数
int main()
{
    scanf("%d %d %d %lld", &n,&m,&b,&mod);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    dp[0][0]=1;
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            for(int k=0; k<=b; k++){
                if(k-a[i]>=0){
                    dp[j][k]=(dp[j][k]+dp[j-1][k-a[i]])%mod;
                }
            }
        }
    }
    LL ans=0;
    for(int k=0; k<=b; k++) ans=(ans+dp[m][k])%mod;
    printf("%lld\n", ans);
    return 0;
}