背包总结
01背包
每种物品要么取(最多取一个),要么不取。
一.求背包能装的最大总价值
int i,j,v[100]={0},w[100]={0},f[1000]={0},n,m;
cin>>n>>m; //n为物品个数,m为最大重量
for(i=1;i<=n;i++)
cin>>v[i]>>w[i]; //v[i]为物品价值,w[i]为物品重量
for(i=1;i<=n;i++){
for(j=m;j>=w[i];j--){
f[j]=MAX(f[j],f[j-w[i]]+v[i]); //f[j]为当背包重量为i时所取物品总价值
}
}
cout<<f[m]<<endl; //f[m]即为当背包重量为m时能取到的最大总价值
二.求装满背包的方案数
int i,j,v[100]={0},w[100]={0},f[1000]={0},n,m;
cin>>n>>m; //n为物品个数,m为最大重量
for(i=1;i<=n;i++)
cin>>v[i]>>w[i]; //v[i]为物品价值,w[i]为物品重量
f[0]=1; //每加到一次f[0]都代表装满一次
for(i=1;i<=n;i++){
for(j=m;j>=w[i];j--){
f[j]=f[j]+f[j-w[i]]; //f[j]为 当前重量的方案数 加上 不取当前物品的方案数
}
}
cout<<f[m]<<endl; //f[m]即为装满时的方案数
完全背包
每种物品不限制取的次数
int i,j,v[100]={0},w[100]={0},f[1000]={0},n,m;
cin>>n>>m;
for(i=1;i<=n;i++)
cin>>v[i]>>w[i];
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){ //这里用正序的原因是可以重复取
f[j]=MAX(f[j],f[j-w[i]]+v[i]); //每次遍历j时,都会把第i个物品在j为各个重量时能取的都取一次
}
}
cout<<f[m]<<endl;
多重背包:
和完全背包一样,只是每件物品有个数限制
int i,j,k=0,v[100]={0},w[100]={0},num[100]={0},f[1000]={0},n,m;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d%d%d",&v[i],&w[i],&num[i]); //num是该物品的个数
for(i=1;i<=n;i++){
for(j=m;j>0;j--){
if(j>=w[i]*num[i]){ //如果能取满该物品
f[j]=MAX(f[j],f[j-num[i]*w[i]]+num[i]*v[i]);
}
else{
k=1; //不能取满就遍历 取几个时值能最大
while(k<num[i] && j>=k*w[i]){
f[j]=MAX(f[j],f[j-k*w[i]]+k*v[i]);
k++;
}
}
}
}
printf("%d\n",f[m]);
5 100
12 8 2
8 7 3
16 11 1
20 15 1
40 34 2
124
这时候忽然想到了该如何求出每种物品取了多少个呢?但是下面这种方法只求出了一种情况,所有情况的话好像也只有枚举了。。反正我是没想出啥好办法,暂时先这样
#include <stdio.h>
int MAX(int a,int b){
return a>b?a:b;
}
void Copy(int x[][100],int a,int b){
int i=1;
for(i=1;i<=5;i++){
x[a][i]=x[b][i];
}
}
int main(){
int i,j,k=0,t=0,v[100]={0},w[100]={0},num[100]={0},f[1000]={0},n,m,x[1000][100]={0}; //x[m][n]是当容量为m时,每样物品取的个数
scanf("%d%d",&n,&m); //物品种类为n,容量为m
for(i=1;i<=n;i++)
scanf("%d%d%d",&v[i],&w[i],&num[i]); //v是该物品的价值,w是物品的重量,num是该物品的个数
for(i=1;i<=n;i++){
for(j=m;j>0;j--){
if(j>=w[i]*num[i]){ //如果能取满该物品
if(f[j]<f[j-num[i]*w[i]]+num[i]*v[i]){
f[j]=f[j-num[i]*w[i]]+num[i]*v[i];
Copy(x,j,j-num[i]*w[i]); //将容量为j-num[i]*w[i]时的方案赋给j时的,然后在此基础上做改变
x[j][i]=num[i]; //这里要明白,x[j][i]中放的是当容量为j时,所取总值最大的方案数,所以x一直是最优解
}
}
else{
k=1; //不能取满就遍历 取几个时值能最大
t=f[j];
while(k<num[i] && j>=k*w[i]){
f[j]=MAX(f[j],f[j-k*w[i]]+k*v[i]);
k++;
}
if(f[j]!=t){
Copy(x,j,j-k*w[i]); //将容量为j-k*w[i]时的方案赋给j时的,然后在此基础上做改变
x[j][i]=k;
}
}
}
}
printf("%d\n",f[m]);
for(i=1;i<=n;i++){
printf("%d ",x[m][i]);
}
return 0;
}
5 100
12 8 2
8 7 3
16 11 1
20 15 1
40 34 2
124
2 0 0 1 2
总结一下:
刷题也刷了不短时间了,但仍然有一些题看着套路很熟悉却不会做。想来想去,还是记录下来,而不是仅仅留存一个程序,就当是给自己再梳理一遍顺便加深一下印象,优化一下当初的程序。