CodeForces 544C (Writing Code)(dp,完全背包)

题意:有n个程序员,要协作写完m行代码,最多出现b个bug,第i个程序员每写一行代码就会产生a[i]个bug,现在问,这n个人合作来写完这m行代码,有几种方案使得出的bug总数不超过b(题中要求总方案数要对一个特定的数取模)?

分析:

这道题目属于dp中的计数类型,即求出方案数。一般来说,为了求出方案数,要将现存的问题分为2个或2个以上完全不重叠的子问题。这就类似于,有n层楼梯,你可以一次跨1阶,或者一次跨2阶,问你有多少种上楼梯的方法一样。

在上楼梯问题中,我们可以在当前状态的第一步跨上1阶,或者在当前状态的第一步跨上2阶,这样,原问题就被分解成了两个完全没有交集的子问题,因为第一步跨法不同,那么不论我们以后怎么上楼梯,都不可能出现相同的走法。即dp[i] = dp[i-1] + dp[i-2]。

那么,对于这道题,我们可以将是否让第i个程序员写代码作为分界标准来分出子问题。

状态定义:dp[i][j][k]--->现在有i个程序员合作,一共有j行代码待完成,最多出k个bug的方案总数。

状态转移方程:dp[i][j][k] = dp[i-1][j][k] + dp[i][j-1][k-a[i]];

注意初始化条件,当j为0时,无论i,k取多少,方案数都为1,即无代码可写,那么我们都有1个方案,就是不派出程序员写代码。

AC代码:(采用了滚动数组)

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define maxn 505
using namespace std;
int dp[2][maxn][maxn];
int ai[maxn];
int main()
{
    int n,m,b,mod;
    while(scanf("%d%d%d%d",&n,&m,&b,&mod) == 4){
        memset(dp,0,sizeof(dp));
        for(int i = 0;i <= 1;++i)
            for(int j = 0;j <= b;++j)
                dp[i][0][j] = 1;
        for(int i = 1;i <= n;++i)
            scanf("%d",&ai[i]);
        for(int i = 1;i <= n;++i)
            for(int j = 1;j <= m;++j)
                for(int k = 0;k <= b;++k){
                    dp[i & 1][j][k] = dp[(i - 1) & 1][j][k] % mod;
                    if(k - ai[i] >= 0) dp[i & 1][j][k] = (dp[i & 1][j][k] + dp[i & 1][j - 1][k - ai[i]]) % mod;
                }
        printf("%d
",dp[n & 1][m][b] % mod);
    }
    return 0;
}

注意,有k<a[i]的情况。

原文地址:https://www.cnblogs.com/tiberius/p/6928142.html