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;
}