lightoj 1231 1232 1233 DP 背包计数

http://www.lightoj.com/volume_showproblem.php?problem=1231

http://www.lightoj.com/volume_showproblem.php?problem=1232

http://www.lightoj.com/volume_showproblem.php?problem=1233

1:

题意:给你n个物品的体积和数量,让你求有多少种组合能恰好装满M体积的背包

开始一直在想怎么用一维数组来捉,弄了半天没什么结果。

所以回归一般的DP,dp[i][j]表示前i种物品,组成j的容量有几种组法

枚举第i种物品的时候可以取一个,取两个,。。,最后别忘了,也可以不取

#include<cstdio>
#include<cstring>
const int mod = 100000007;
const int maxn = 1010;
int dp[55][maxn];
int a[55],b[55];
int main()
{
	int t,ca=1,n,m;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		for(int i=1;i<=n;i++) scanf("%d",&b[i]);
		memset(dp,0,sizeof(dp));
		dp[0][0]=1;
		for(int i=1;i<=n;i++)
		{
			for(int j=m;j>=0;j--)
			{
				for(int k=1;k<=b[i];k++)
				{
					if(j-k*a[i]>=0)
					 dp[i][j]+=dp[i-1][j-k*a[i]];
				}
			}
			for(int j=0;j<=m;j++) dp[i][j]+=dp[i-1][j],dp[i][j]%=mod;
		}
		printf("Case %d: %d\n",ca++,dp[n][m]);
	}
	return 0;
}

2:

题意和上题一样,只不过背包容量的范围变成了10000,每种物品的数量也是10000数量级

仔细观察上一题的dp转移过程,我们可以发现上一层的状态在反复的加,所以可以考虑优化,具体见代码

#include<cstdio>
#include<cstring>
int dp[110][10010];
int sum[10010],a[10010];
const int mod = 100000007;
int main()
{
    int t,ca=1,n,K;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&K);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        memset(dp[0],0,sizeof(dp[0]));
        dp[0][0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=K;j++)
            {
                dp[i][j]=0;sum[j]=0;
                if(j-a[i]>=0) sum[j]+=sum[j-a[i]];
                sum[j]+=dp[i-1][j];
                sum[j]%=mod;
                if(j-a[i]>=0) dp[i][j]+=sum[j-a[i]];
                dp[i][j]+=dp[i-1][j];
                dp[i][j]%=mod;
            }
        }
        printf("Case %d: %d\n",ca++,dp[n][K]);
    }
    return 0;
}

另有一种节省空间的做法


#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
 
#define MAXK 10000
#define MAXN 100
#define MOD  100000007
 
#define Zero(v) memset(v, 0, sizeof(v))
 
// dp[i] will store the number of ways to make i with the coins
int dp[MAXK + 1];
 
int A[MAXN];
int n, K;
 
void run_dp()
{
    Zero(dp);
    dp[0] = 1;
 
    for (int i = n - 1; i >= 0; --i)
        for (int a = 0, b = A[i]; b <= K; ++a, ++b)
            dp[b] = (dp[b] + dp[a]) % MOD;
}
 
int main()
{
    int T;
    scanf("%d", &T);
 
    int ncase = 0;
    while (T--) {
        scanf("%d%d", &n, &K);
 
        for (int i = 0; i < n; ++i)
            scanf("%d", &A[i]);
 
        run_dp();
 
        printf("Case %d: %d\n", ++ncase, dp[K]);
    }
 
    return 0;
}


3:

这题就属于简单题了,直接用多重背包可行性的方法来做

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[101],num[101];
bool dp[100001];
int used[1000101];
int main()
{
    int t,ca=1,n,m;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)   scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)   scanf("%d",&num[i]);
        memset(dp,0,sizeof(bool) * (m+1));
        dp[0]=true;
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            memset(used,0,sizeof(int)*(m+1));
            for(int j=a[i];j<=m;j++)
            {
                if(!dp[j] && dp[j-a[i]] && used[j-a[i]]<num[i])
                {
                    ans++;
                    used[j]=used[j-a[i]]+1;
                    dp[j]=true;
                }
            }
        }
        printf("Case %d: %d\n",ca++,ans);
    }
    return 0;
}



你可能感兴趣的:(lightoj 1231 1232 1233 DP 背包计数)