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