为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾
区,现在假设你一共有资金 n 元,而市场有 m 种大米,每种大米都是袋装产品,
其价格不等,并且只能整袋购买。请问:你用有限的资金最多能采购多少公斤粮
食呢?
输入:
输入数据首先包含一个正整数 C,表示有 C 组测试用例,每组测试用例的第
一行是两个整数 n 和 m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的
种 类 , 然 后 是 m 行 数 据 , 每 行 包 含 3 个 数 p , h 和
c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应
种类大米的袋数。
输出:
对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不
光所有的大米,并且经费你可以不用完。每个实例的输出占一行。
样例输入:
1
8 2
2 100 4
4 100 2
样例输出:
400
我们可以将该问题转化为0-1背包问题,其时间复杂度为 O ( c ∗ ∑ i = 1 n k i ) O(c*\sum\limits_{i=1}^nk_i) O(c∗i=1∑nki).当然该办法已经可以解决问题,但但对于k的规模较大的情况下可以采用更好的发来来进行优化。这个就得联系到之前涉及到的二进制拆解。举个例子,对于k=7,其实我们没有必要把这个对象存储7次,反之只需要存储1,2,4这三种组合便可以组合出1~7之间的任意情况了。因此对于更一般的情况k,可以将其拆解为 1 , 2 , 4 , . . . , k − 2 c + 1 1,2,4,...,k-2^c+1 1,2,4,...,k−2c+1,其中 c = ⌊ log 2 k ⌋ c=\lfloor \log_2^k\rfloor c=⌊log2k⌋,通过此番拆解我们可以将算法的时间复杂度降到 O ( c ∗ ∑ i = 1 n l o g 2 ( k i ) ) O(c*\sum\limits_{i=1}^nlog_2(k_i)) O(c∗i=1∑nlog2(ki)).
#include
#include
#define M 2001
#define N 101
using namespace std;
struct rice{
int price;
int weight;
};
int IntPow(int x,int y){
int ans=1;
for(int i=1;i<=y;i++){
ans*=x;
}
return ans;
}
int max(int a,int b){return a>b?a:b;}
int dp[N];
rice list[M];
int main()
{
int C;
scanf("%d",&C);
int n,m;
int cnt=1;
for(int Case=1;Case<=C;Case++){
cnt=1;
scanf("%d%d",&n,&m);
int p,h,c;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&p,&h,&c);
int bound=int(log(c)/log(2));
for(int j=0;j<bound;j++){
list[cnt].price=IntPow(2,j)*p;
list[cnt].weight=IntPow(2,j)*h;
cnt++;
}
if(c-IntPow(2,bound)+1!=0){
list[cnt].price=(c-IntPow(2,bound)+1)*p;
list[cnt].weight=(c-IntPow(2,bound)+1)*h;
cnt++;
}
}
// for(int i=1;i
// printf("price=%d,weight=%d\n",list[i].price,list[i].weight);
// }
for(int i=0;i<cnt;i++)dp[i]=0;//初始化
for(int i=1;i<cnt;i++){
for(int j=n;j>=list[i].price;j--){
dp[j]=max(dp[j],dp[j-list[i].price]+list[i].weight);
}
}
printf("%d\n",dp[n]);
}
return 0;
}
/*
1
8 2
2 100 4
4 100 2
*/
下面这个版本的完全背包代码更加的简洁
#include
#define N 100+5
using namespace std;
int dp[N];
int main()
{
int C,n,m;
cin>>C;
while(C--){
cin>>n>>m;
int p,w,num;
for(int i=0;i<=n;i++)dp[i]=0;
for(int i=1;i<=m;i++){
cin>>p>>w>>num;
if(p*num>=n){//完全背包
for(int j=p;j<=n;j++){
dp[j]=max(dp[j],dp[j-p]+w);
}
}
else{//多重背包
for(int k=1;num-k>=0;k*=2){
for(int j=n;j>=p*k;j--){
dp[j]=max(dp[j],dp[j-p*k]+w*k);
}
num-=k;
}
if(num>0){
for(int j=n;j>=p*num;j--){
dp[j]=max(dp[j],dp[j-p*num]+w*num);
}
}
}
}
cout<<dp[n]<<endl;
}
return 0;
}
/*
1
8 2
2 100 4
4 100 2
*/