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