求解0/1背包问题
穷举法
问题描述:
问题求解:对于n个物品,容量为W的背包问题。
<1>先采用求幂集的方法求出所有的物品组合。
<2>对于每一种组合,计算组合中物品的总重量sumw,总价值sumv。
<3>对于每一种组合,如果sumw <= W ,说明该组合是一种解。比较所有解,将最佳方案保存在 maxsumw 和 maxsumv 中。输出所有解与最佳解。
#include<stdio.h> #define Maxn 10 #define MaxSize 1000 typedef struct{ //幂集类型 int data[MaxSize][Maxn]; //data[i][0] 表示该子集的长度 int n; //幂集的个数 }pSetType; void copy(int a[],int b[],int m){ //将a[0...m]复制到b[0...m] int i; for(i=0;i<=m;i++) b[i] = a[i]; } void pset(int n,pSetType &p){ //求1~n的幂集p int i,j,m; int a[Maxn]; p.data[0][0] = 0; p.n = 1; for(i=1;i<=n;i++){ m = p.n; for(j=0;j<m;j++){ copy(p.data[j],a,p.data[j][0]); a[0]++; a[a[0]] = i; copy(a,p.data[p.n],a[0]); p.n++; } } } void knap(pSetType p,int w[],int v[],int W){ int i,j; int sumw,sumv; int maxi,maxsumw = 0,maxsumv = 0; printf(" 序号\t选中物品\t总重量\t总价值\t能否装入\n"); for(i=0;i<p.n;i++){ printf(" %d\t",i+1); sumw = sumv = 0; printf("{"); for(j=1;j <= p.data[i][0];j++){ printf("%d",p.data[i][j]); sumv += v[p.data[i][j] - 1]; sumw += w[p.data[i][j] - 1]; } printf("}\t\t%d\t%d\t",sumw,sumv); if(sumw<=W){ printf("能\n"); if(sumv > maxsumv){ maxsumw = sumw; maxsumv = sumv; maxi = i; } } else printf("否\n"); } printf("最佳方案: "); printf("选中物品: "); printf("{"); for(j=1;j<=p.data[maxi][0];j++) printf("%d",p.data[maxi][j]); printf("},"); printf("总重量 = %d,总价值 = %d\n",maxsumw,maxsumv); } int main(){ int n=4,W = 7; int w[] = {5,3,2,1}; int v[] = {4,4,3,1}; pSetType p; pset(n,p); printf("0/1背包的求解方案\n",n); knap(p,w,v,W); printf("\n"); return 0; }
动态规划
//求解0_1背包问题 //动态规划 #include<stdio.h> #define MaxN 20 #define MaxW 100 int knap(int f[MaxN][MaxW],int w[],int v[],int W,int n){ //动态规划求数组f[][] int i,r; for(i=0;i<=n;i++) f[i][0] = 0; for(r=0;r<=W;r++) f[0][r] = 0; for(i=1;i<=n;i++){ for(r=1;r<=W;r++){ if(r < w[i]) f[i][r] = f[i-1][r]; else{ if(f[i-1][r] < f[i-1][r-w[i]] + v[i]) f[i][r] = f[i-1][r-w[i]] + v[i]; else f[i][r] = f[i-1][r]; } } } return f[n][W]; } int Traceback(int f[MaxN][MaxW],int w[],int x[],int W,int n){ int i,r=W; int maxw = 0; for(i=n;i>0;i--){ if(f[i][r] != f[i-1][r]){ x[i] = 1; maxw += w[i]; r = r - maxw; } else x[i] = 0; } return maxw; } void dispknap(int x[],int maxw,int maxv,int n){ int i; printf("最佳背包方案是:\n"); for(i=0;i<=n;i++){ if(x[i] == 1) printf("选取第%d种物品\n",i); } printf("总重量=%d,总价值=%d",maxw,maxv); } int main(){ int f[MaxN][MaxW]; int x[MaxN]; int maxv; int maxw; int n=5,W=10; int w[MaxN] = {0,2,2,6,5,4}; int v[MaxN] = {0,6,3,5,4,6}; maxv = knap(f,w,v,W,n); maxw = Traceback(f,w,x,W,n); dispknap(x,maxw,maxv,n); return 0; }
回溯法
#include<stdio.h> #define MAXN 20 int maxw; int maxv; int x[MAXN]; void knap(int w[],int v[],int W,int n,int i,int tw,int tv,int op[]){ int j; if(i>n){ if(tw<W && tv>maxv){ maxv = tv; maxw = tw; for(j=1;j<=n;j++) x[j] = op[j]; } } else{ op[i] = 1; knap(w,v,W,n,i+1,tw+w[i],tv+v[i],op); op[i] = 0; knap(w,v,W,n,i+1,tw,tv,op); } } void disp(int x[],int n){ int i; printf("最佳方案是:\n"); for(i=1;i<=n;i++){ if(x[i] == 1) printf("选取第%d个物品\n",i); } printf("总重量 = %d,总价值 = %d",maxw,maxv); } int main(){ int n=4; int W = 7; int op[MAXN]; int w[] = {0,5,3,2,1}; int v[] = {0,4,4,3,1}; knap(w,v,W,n,1,0,0,op); disp(x,n); return 0; }
左剪枝
void knap(int w[],int v[],int W,int n,int i,int tw,int tv,int op[]){
int j;
if(i>n){
if(tw<W && tv>maxv){
maxv = tv;
maxw = tw;
for(j=1;j<=n;j++)
x[j] = op[j];
}
}
else{
if(tw+w[i] < W)
{
op[i] = 1;
knap(w,v,W,n,i+1,tw+w[i],tv+v[i],op);
}
op[i] = 0;
knap(w,v,W,n,i+1,tw,tv,op);
}
}
右剪枝
void knap(int w[],int v[],int W,int n,int i,int tw,int tv,int op[]){ int j,m; if(i>n){ if(tw<W && tv>maxv){ maxv = tv; maxw = tw; for(j=1;j<=n;j++) x[j] = op[j]; } } else{ if(tw+w[i] < W) { op[i] = 1; knap(w,v,W,n,i+1,tw+w[i],tv+v[i],op); } op[i] = 0; m=0; for(j=0;j<i;j++) if(op[j] == 0) m++; if(m<=1) knap(w,v,W,n,i+1,tw,tv,op); } }
#include<stdio.h>#define Maxn 10#define MaxSize 1000typedef struct{//幂集类型int data[MaxSize][Maxn];//data[i][0] 表示该子集的长度int n;//幂集的个数}pSetType;void copy(int a[],int b[],int m){//将a[0...m]复制到b[0...m]int i;for(i=0;i<=m;i++)b[i] = a[i];}void pset(int n,pSetType &p){//求1~n的幂集pint i,j,m;int a[Maxn];p.data[0][0] = 0;p.n = 1;for(i=1;i<=n;i++){m = p.n;for(j=0;j<m;j++){copy(p.data[j],a,p.data[j][0]);a[0]++;a[a[0]] = i;copy(a,p.data[p.n],a[0]);p.n++; }}}void knap(pSetType p,int w[],int v[],int W){int i,j;int sumw,sumv;int maxi,maxsumw = 0,maxsumv = 0;printf("序号\t选中物品\t总重量\t总价值\t能否装入\n");for(i=0;i<p.n;i++){printf("%d\t",i+1);sumw = sumv = 0;printf("{");for(j=1;j <= p.data[i][0];j++){printf("%d",p.data[i][j]);sumv += v[p.data[i][j] - 1];sumw += w[p.data[i][j] - 1]; }printf("}\t\t%d\t%d\t",sumw,sumv);if(sumw<=W){printf("能\n");if(sumv > maxsumv){maxsumw = sumw;maxsumv = sumv;maxi = i;} }elseprintf("否\n");} printf("最佳方案: ");printf("选中物品: ");printf("{");for(j=1;j<=p.data[maxi][0];j++)printf("%d",p.data[maxi][j]);printf("},");printf("总重量 = %d,总价值 = %d\n",maxsumw,maxsumv);}int main(){int n=4,W = 7;int w[] = {5,3,2,1};int v[] = {4,4,3,1};pSetType p;pset(n,p);printf("0/1背包的求解方案\n",n);knap(p,w,v,W);printf("\n");return 0;}