Time Limit: 1000 MS Memory Limit: 32768 KB
Description
XXX上山去采药。XXX有一个容量为m(1<=m<=1000)的背包,他所采集的药材的总重量不能大于背包的容量。已知共有n(1<=n<=1000 )种药材,每种药材都有无限多,并且知道每种药材的重量w(1<=w<=m)及价值v(1<=v<=100000),如何选择,才能使得采到的药材的总价值最大?
Input
第1行为两个整数m和n,分别为背包的容量及药材的种数。 第2至n+1行每行两个整数w和v,分别表示每种药材的重量及价值。
Output
能采到的药材的最大总价值
Sample Input
100 5
77 92
33 50
34 60
50 46
99 161
Sample Output
161
完全背包问题相对于01背包问题就是多了一个条件——每个物品可以无限拿,那我们很容易就能想到,每个容量的背包如果装的下一种物品,那么我们就要尽可能多得拿这种物品以保证当前的总价值最大。
#include<iostream>
using namespace std;
const int T=1005;//时间(背包容量)
const int M=1005;//药材种类
int dp[M][T];//状态数组 ,dp[M][T]代表容量为T的背包装编号为M的药材时的最大价值
struct data{
int time;//采集药材要占用的时间
int value;//药材的价值
}grass[M];//存放药材数据的结构体数组
int max(int x,int y)
{
return x>y?x:y;
}
int main()
{
//状态数组中的元素已经自动被初始化为了0 ,故不再重复初始化
int t,m;
cin>>t>>m;
for(int i=1;i<=m;++i)
{
cin>>grass[i].time>>grass[i].value;//读入药材数据并对他们编号
}
for(int i=1;i<=m;++i)
{
for(int j=1;j<=t;++j)
{
if(j>=grass[i].time)//如果能采集当前的药材
{
int k=j/grass[i].time;//每个药材都尽可能多的拿
//for(int k=0;k<=j/grass[i].time;++k)
dp[i][j]=max(grass[i].value*k+dp[i-1][j-k*grass[i].time],dp[i-1][j]);
//比较装下当前药材的总价值和不装当前药材总价值的大小,取大的
}
else//如果不能采集当前的药材
{
dp[i][j]=dp[i-1][j];//那么和此容量的背包装上一个药材时的最大价值相同
}
}
}
cout<<dp[m][t]<<endl;//输出答案
return 0;
}
下面是状态压缩版:
#include<iostream>
using namespace std;
const int T=1005;//时间(背包容量)
const int M=1005;//药材种类
int dp[T];//状态数组 ,dp[M][T]代表容量为T的背包装编号为M的药材时的最大价值
struct data{
int time;//采集药材要占用的时间
int value;//药材的价值
}grass[M];//存放药材数据的结构体数组
int max(int x,int y)
{
return x>y?x:y;
}
int main()
{
//状态数组中的元素已经自动被初始化为了0 ,故不再重复初始化
int t,m;
cin>>t>>m;
for(int i=1;i<=m;++i)
{
cin>>grass[i].time>>grass[i].value;//读入药材数据并对他们编号
}
for(int i=1;i<=m;++i)
{
for(int j=t;j>=grass[i].time;--j)
{
int k=j/grass[i].time;//每个药材都尽可能多的拿
dp[j]=max(grass[i].value*k+dp[j-k*grass[i].time],dp[j]);
//比较装下当前药材的总价值和不装当前药材总价值的大小,取大的
}
}
cout<<dp[t]<<endl;//输出答案
return 0;
}
可以一边读入数据,一边动态更新dp数组,将药材数据存起来在这道题中完全没有必要。
#include<iostream>
using namespace std;
const int T=1005;//时间(背包容量)
const int M=1005;//药材种类
int dp[T];//状态数组 ,dp[M][T]代表容量为T的背包装编号为M的药材时的最大价值
int max(int x,int y)
{
return x>y?x:y;
}
int main()
{
//状态数组中的元素已经自动被初始化为了0 ,故不再重复初始化
int t,m;
int time,value;
cin>>t>>m;
for(int i=1;i<=m;++i)
{
cin>>time>>value;
for(int j=t;j>=time;--j)
{
int k=j/time;//每个药材都尽可能多的拿
dp[j]=max(value*k+dp[j-k*time],dp[j]);
//比较装下当前药材的总价值和不装当前药材总价值的大小,取大的
}
}
cout<<dp[t]<<endl;//输出答案
return 0;
}
前面的写法可能是错的(毕竟是自己造的),下面的写法是老师教的:
#include<iostream>
using namespace std;
const int T=1005;//时间(背包容量)
const int M=1005;//药材种类
int dp[T];//状态数组 ,dp[M][T]代表容量为T的背包装编号为M的药材时的最大价值
int max(int x,int y)
{
return x>y?x:y;
}
int main()
{
//状态数组中的元素已经自动被初始化为了0 ,故不再重复初始化
int t,m;
int time,value;
cin>>t>>m;
for(int i=1;i<=m;++i)
{
cin>>time>>value;
for(int j=time;j<=t;++j)
{
dp[j]=max(value+dp[j-time],dp[j]);
}
}
cout<<dp[t]<<endl;//输出答案
return 0;
}
其实就是把状态压缩后的01背包的内层循环变成了从小到大枚举,这样做实际上在计算每个容量的背包的时候用到的数据都是加了这一层那个物品的数据,为什么这样做是正确的呢?模拟一下就可以想到,如果当前背包的容量可以装下一个这一层的物品,那么它用到的数据是装不下这一层的东西的数据(就是装了0个这一层的物品),根据01背包我把1个这一层的东西装进去了,那么当前背包中这一层的物品就只装了0+1=1个;如果当前背包的容量可以装下两个这一层的物品,根据01背包,我只把1个这一层的物品装进去了,但是我用到的数据已经装了一个这一层的东西,这样就相当于我装了两个这一层的东西,后面的情况以此类推,这样就保证了每个容量的背包的解是正确的。