01背包:
有n 种不同的物品,每个物品有两个属性,size 体积,value 价值,现在给一个容量为 w 的背包,问最多可带走多少价值的物品。
例:编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,每件物品数量只有一个,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
考虑f[5][10]即表示为5种都可以放,剩余重量为10,的时候的价值。
假设放入a,那么f[5][10]=max{ f[4][10], (f[4][10-weight[a]]+value[a]) },比较不放a和放入a的价值,选择最大的赋值给f[5][10];(接着递归)
| Weigh | Value | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
a | 2 | 6 | 0 | 0 | 6(a) | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
b | 2 | 3 | 0 | 0 | 6 | 6 | 9(ab) | 9 | 9 | 9 | 9 | 9 | 9 |
c | 6 | 5 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 11ac | 11 | 14abc |
d | 5 | 4 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 10ad | 11 | 13abd | 14 |
e | 4 | 6 | 0 | 0 | 6 | 6 | 9 | 9 | 12ae | 12 | 15abe | 15 | 15 |
#include <iostream> using namespace std; int findmax(int *weigh,int *value,int *flag,int n,int w){ int **f=new int*[n]; int maxvalue=0; for(int i=0;i<n;i++){//n行w+1列每列0 1 2 3 4 5 6 7 8 9 10
f[i]=new int[w+1]; } for(int i=0;i<n;i++){ for(int j=0;j<w+1;j++){ f[i][j]=0;//初始化0 } } for(int i = 1; i <= w; i++) { f[0][i] = (i < weigh[0])?0:value[0];///只放第一个物品时的价值,即第一行 } for(int i=1;i<n;i++){//第2行开始, for(int j=1;j<w+1;j++){ if(j>=weigh[i]){///如果当前允许承重y大于当前物品的重量,那么比较放或不放两种情况
f[i][j]=max( f[i-1][j] , (f[i-1][j-weigh[i]] + value[i])); } else{///不放 f[i][j]=f[i-1][j]; } } } maxvalue=f[n-1][w];//最大值就是最后一个数 int k=n-1; int maxw=w; while(k){//逆过程,找到哪些放了哪些没放 j!=0 if(f[k][maxw]==(f[k-1][maxw-weigh[k]]+value[k])){ flag[k]=1; maxw-=weigh[k]; } k–; } if(f[0][maxw]){//如果不为0,说明第一个也有 flag[0]=1; } return maxvalue; }
int main(){ int n,w;///n表示多少种,w表示背包最大承重 int ww,vv; cin>>n>>w; int *weigh = new int[n];//各个物品的重量 int *value = new int[n];//各个物品的价值 int *flag= new int[n];//结果 0表示不放,1表示放
int maxvalue; for(int i=0;i<n;i++){ cin>>ww>>vv; weigh[i]=ww;value[i]=vv; flag[i]=0; } maxvalue=findmax(weigh,value,flag,n,w); cout<<maxvalue<<endl; for(int i=0;i<n;i++){ cout<<flag[i]<<” “; } return 0; }
|
输入示例:
5 10 //5种物品,背包承重为10 2 6 //第一种物品,重量为2,价值为1,数目1个 2 3 6 5 5 4 4 6 |
输出:
15 1 1 0 0 1 |
完全背包问题:
例:有编号分别为a,b,c,d的四件物品,它们的重量分别是2,3,4,7,它们的价值分别是1,3,5,9,每件物品数量无限个,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
方1:可以简单思路:他们的单位价值为0.5 1 1.25 1.271 放d越多越好,7+3=10 9+3=12
方2:可以也根据上面的01背包变化来,他们的区别在于个数
考虑f[5][10]即表示为5种都可以放,剩余重量为10,的时候的价值。
假设放入a,那么f[5][10]=max{ f[4][10], (f[5][10-weight[a]]+value[a]) },比较不放a和放入a的价值,选择最大的赋值给f[5][10];(接着递归)(这里是放f[5]而不是f[4],与01背包不同,因为可能还可以放a)
初始化时,当只考虑一件物品a时,f[1][j] =( j/weight[a])*value[a]。
| Weigh | Value | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
a | 2 | 1 | 0 | 0 | 1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 |
b | 3 | 3 | 0 | 0 | 1 | 3 | 3 | 4 | 6 | 6 | 7 | 9 | 9 |
c | 4 | 5 | 0 | 0 | 1 | 3 | 5 | 5 | 6 | 8 | 10 | 10 | 11 |
d | 7 | 9 | 0 | 0 | 1 | 3 | 5 | 5 | 6 | 9 | 10 | 10 | 12 |
#include <iostream> using namespace std; int findmax(int *weigh,int *value,int *flag,int n,int w){ int **f=new int*[n]; int maxvalue=0; for(int i=0;i<n;i++){ f[i]=new int[w+1]; } for(int i=0;i<n;i++){ for(int j=0;j<w+1;j++){ f[i][j]=0; } } for(int i = 1; i <= w; i++) { f[0][i] = (i < weigh[0])?0:((i/weigh[0])*value[0]);//与01背包不同 } for(int i=1;i<n;i++){ for(int j=1;j<w+1;j++){ if(j>=weigh[i]){ f[i][j]=max( f[i-1][j] , (f[i][j-weigh[i]] + value[i]));///与01背包不同 } else{ f[i][j]=f[i-1][j]; } } } maxvalue=f[n-1][w]; int k=n-1; int maxw=w; while(k){ if(f[k][maxw]==(f[k][maxw-weigh[k]]+value[k])){///不同 flag[k]++; maxw-=weigh[k]; } k–; } flag[0] = f[0][maxw]/value[0];///不同 return maxvalue; }
int main(){ int n,w; int ww,vv; cin>>n>>w; int *weigh=new int[n]; int *value=new int[n]; int *flag =new int[n]; int maxvalue; for(int i=0;i<n;i++){ cin>>ww>>vv; weigh[i]=ww;value[i]=vv; flag[i]=0; } maxvalue=findmax(weigh,value,flag,n,w); cout<<maxvalue<<endl; for(int i=0;i<n;i++){ cout<<flag[i]<<” “; } return 0; }
| //也可以使用方法1: int findmax(int *weigh,int *value,int *flag,int n,int w){ int maxvalue=0; double *a=new double[n]; for(int i=0;i<n;i++){ //假设i小单位价值小 a[i]=value[i]/weigh[i]; } int m=n-1; int y=w; while(m>=0){ if(weigh[m]<=y){ y=y-weigh[m]; maxvalue+=value[m]; flag[m]++; } else{ m–; } } return maxvalue; }
|
输入示例:
4 10 //4种物品,背包承重为10 2 1 //重量,价值,数目不限 3 3 4 5 7 9 |
输出:
12 0 1 0 1 |
多重背包问题:
例:有编号分别为a,b,c的三件物品,它们的重量分别是1,2,2,它们的价值分别是6,10,20,他们的数目分别是10,5,2,现在给你个承重为 8 的背包,如何让背包里装入的物品具有最大的价值总和?
多重背包和01背包、完全背包的区别:多重背包中每个物品的个数都是给定的,可能不是一个,也不是无限个。
由于每个物品多了数目限制,因此初始化和递推公式都需要更改一下。初始化时,只考虑一件物品a时,f[1][j] = min{num[1], j/weight[1]}。 计算考虑i件物品承重限制为y时最大价值f[i][y]时,递推公式考虑两种情况,要么第 i 件物品一件也不放,就是f[i-1][y], 要么第 i 件物品放 k 件,其中 1 <= k <= (y/weight[i]),考虑这一共 k+1 种情况取其中的最大价值即为f[i][y]的值,即f[i][y] = max{f[i-1][y], (f[i-1][y-k*weight[i]]+k*value[i])}。 需要判断第 i 件物品的个数是否超过限制数量 num[i]。
| Weigh | Value | Num | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
a | 1 | 6 | 10 | 0 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48(8a) |
b | 2 | 10 | 5 | 0 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 |
c | 2 | 20 | 2 | 0 | 6 | 20 | 26 | 40 | 46 | 52 | 58 | 64(2c4a) |
#include <iostream> using namespace std; int findmax(int *weigh,int *value,int *num,int *flag,int n,int w){ int **f=new int*[n]; int maxvalue=0; for(int i=0;i<n;i++){ f[i]=new int[w+1]; } for(int i=0;i<n;i++){ for(int j=0;j<w+1;j++){ f[i][j]=0; } } for(int i = 1; i <= w; i++) { int count = min(num[0], i/weigh[0]); f[0][i] = (i < weigh[0])?0:(count * value[0]);//此处不同 } for(int i=1;i<n;i++){ for(int j=1;j<w+1;j++){ if(j>=weigh[i]){///此if块不同 int count = min(num[i], j/weigh[i]); f[i][j] = f[i-1][j]; for(int k = 1; k <= count; k++) { int temp = f[i-1][j-weigh[i]*k] + k*value[i]; if(temp >= f[i][j]) f[i][j] = temp; } } else{ f[i][j]=f[i-1][j]; } } } maxvalue=f[n-1][w]; int k=n-1; int maxw=w; while(k){ int count = min(num[k], maxw/weigh[k]); for(int q=count;q>0;q–){ if(f[k][maxw] == (f[k-1][maxw-weigh[k]*q]+q*value[k])) { flag[k] = q; maxw = maxw – q*weigh[k]; break; } } k–; } flag[0] = f[0][maxw]/value[0];///不同 return maxvalue; }
int main(){ int n,w; int ww,vv,nn; cin>>n>>w; int *weigh=new int[n]; int *value=new int[n]; int *flag =new int[n]; int *num=new int[n]; int maxvalue; for(int i=0;i<n;i++){ cin>>ww>>vv>>nn; weigh[i]=ww;value[i]=vv;num[i]=nn; flag[i]=0; } maxvalue=findmax(weigh,value,num,flag,n,w); cout<<maxvalue<<endl; for(int i=0;i<n;i++){ cout<<flag[i]<<” “; } return 0; }
|
输入示例:
3 8 1 6 10 2 10 5 2 20 2 |
输出:
64 4 0 2 |
参考:https://blog.csdn.net/na_beginning/article/details/62884939