Time Limit: 1 second
Memory Limit: 128 MB
【问题描述】
设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大。
【输入格式】
第一行:两个整数,M(背包容量,M<=200)和N(物品数量,N<=30);
第2..N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。
【输出格式】
仅一行,一个数,表示最大总价值。
Sample Input
10 4 2 1 3 3 4 5 7 9
【题解】
【法一】
设f[i][j]表示前i个物品,所用的容量不超过j所能获得的最大价值。
f[i][j] = max{f[i-1][j],f[i-1][j-k*w[i]] + k*c[i]}; (k*w[i] <=j)
对于第i个物品,可以选择不拿(f[i-1][j]),也可以任意选择k个。
这种二维的存储方式比较容易理解。
#include <cstdio> #include <cstring> int m,n,f[31][250],w[50],c[50]; void input_data() { scanf("%d%d",&m,&n); for (int i = 1;i <= n;i++) //êäèë¸÷¸öÎïÆ·μÄDÅÏ¢ scanf("%d%d",&w[i],&c[i]); } void get_ans() { memset(f,0,sizeof(f)); for (int i = 1;i <= n;i++) for (int j = m;j >= 0;j--) { f[i][j] = f[i-1][j]; int k = 1; while (k*w[i] <= j) { if (f[i][j] < f[i-1][j-k*w[i]] + k*c[i]) f[i][j] = f[i-1][j-k*w[i]] + k*c[i]; k++; } } } void output_ans() { printf("max=%d",f[n][m]); } int main() { //freopen("F:\rush.txt","r",stdin); input_data(); get_ans(); output_ans(); return 0; }【法二】
设f[j]表示容量不超过j的背包所能装得的最大价值。
f[j] = max{f[j],f[j-w[i]] + c[i]};
这次j层循环要正向进行。
假设一个物品的重量为1.
你更新完f[5],又可以更新f[6],f[7]...这样就达到了每个物品可以多次拿的效果。
#include <cstdio> #include <cstring> int m,n,f[250],w[50],c[50]; void input_data() { scanf("%d%d",&m,&n); for (int i = 1;i <= n;i++) scanf("%d%d",&w[i],&c[i]); } void get_ans() { memset(f,0,sizeof(f)); for (int i = 1;i <= n;i++) for (int j = w[i];j <= m;j++) //一定要正向循环。 if (f[j-w[i]] + c[i] > f[j]) //这里就省掉了一层k循环。 f[j] = f[j-w[i]] + c[i]; } void output_ans() { printf("max=%d",f[m]); } int main() { //freopen("F:\rush.txt","r",stdin); input_data(); get_ans(); output_ans(); return 0; }