01背包问题代码整理

以下是我收集并整理的 "01背包问题" 的c代码,已经在Dev-C++中通过编译。

//================== bei_bao_01.cpp ===========================

#include <stdio.h>
#include <stdlib.h>

/*
背包问题


01背包: 有N件物品和一个重量为M的背包。(每种物品均只有一件)第i件物品的重量是w[i],价值是p[i]。
        求解将哪些物品装入背包可使价值总和最大。


完全背包: 有N种物品和一个重量为M的背包,每种物品都有无限件可用。第i种物品的重量是w[i],价值是p[i]。
          求解将哪些物品装入背包可使这些物品的费用总和不超过背包重量,且价值总和最大。


多重背包: 有N种物品和一个重量为M的背包。第i种物品最多有n[i]件可用,每件重量是w[i],价值是p[i]。
          求解将哪些物品装入背包可使这些物品的费用总和不超过背包重量,且价值总和最大。


01背包问题:


这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。


用子问题定义状态:即c[i][v]表示前i件物品恰放入一个重量为m的背包可以获得的最大价值。则其状态转移方程便是:


c[i][m]=max{c[i-1][m],c[i-1][m-w[i]]+p[i]}


这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下:“将前i件物品放入重量为m的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为c[i-1][m];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的重量为m-w[i]的背包中”,此时能获得的最大价值就是c[i-1][m-w[i]]再加上通过放入第i件物品获得的价值p[i]。


**************************************************************************
问题描述:
      有 n 件物品x1, x2, …, xn , 每件物品有一个价值和一个重量,分别记为: 
      v1,v2, …vn 
      w1,w2, …wn 
其中所有的 wi 均为整数。 现有一个背包,其最大载重量为m,要求从这n件物品中任取若干件(这些物品每样只有一件,要么被装入要么被留下)。问背包中装入哪些物品可使得所装物品的价值和最大? (我们只需要求出最大价值,不需要求出具体拿的是哪些物品)
例如,m=23, n = 5, 
        vi : 19 24 33 45 50 
        wi : 5   6 8   11 12 
最大价值为:95
**************************************************************************


输入:
10      //背包容量,即最多能放10件物品 
3       //物品数量,即总共有3件物品 
3 4 5   //每个物品的重量 
4 5 6   //每个物品的价值


输出:
能取得的最大价值 
 
*/


int** Pack_01(int m, int n, int w[], int p[]);
int* Print_Pack_01(int** c, int w[], int m, int n);


int main(int argc, char* argv[])
{
int m=10;
int n=4;
int w[]={5,1,4,3};
int p[]={40,10,25,30};

int** c = Pack_01(m, n, w, p);
int* x = Print_Pack_01(c, w, m, n);

// for(int i=0; i<n; i++)
// printf("%d\n",x[i]);


for(int i=0; i<n+1; i++)
free(c[i]);
free(c);
free(x);

system("pause");

return 0;
}


//=========================================================
int** Pack_01(int m, int n, int w[], int p[])
{  
//c[i][j]表示前i件物品恰放入一个重量为m的背包可以获得的最大价值  
int** c;
c=(int**)malloc((n+1)*sizeof(int*));
for(int i=0; i<(n+1); i++)
    {
c[i]=(int*)malloc((m+1)*sizeof(int));
    }

for(int r=0; r<n+1; r++)
{
for(int q=0; q<m+1; q++)
{
c[r][q]=0;
}
}
     
for(int i=1; i<n+1; i++) //枚举n件物品
{
for(int j=0; j<m+1; j++) //枚举所有的装入情况,背包容量 m=10 
{
//当物品为i件重量为j时,如果第i件的重量(w[i-1])小于重量j时,c[i][j]为下列两种情况之一:  
//(1)物品i不放入背包中,所以c[i][j]为c[i-1][j]的值  
//(2)物品i放入背包中,则背包剩余重量为j-w[i-1],所以c[i][j]为c[i-1][j-w[i-1]]的值加上当前物品i的价值 

c[i][j] = c[i-1][j]; //先让本次装入结果等于上次结果
if(j>=w[i-1] && ((c[i-1][j-w[i-1]]+p[i-1])>c[i][j])) //如果能装第i件物品,且装入后价值变大则装入
{
c[i][j] = c[i-1][j-w[i-1]]+p[i-1];
}
}
}

//================================
for(int r=0; r<n+1; r++)
{
for(int q=0; q<m+1; q++)
{
printf("%d ",c[r][q]);
}
printf("\n");
}
printf("\n=======\n");
printf("背包容量: m=%d\n",m);
printf("物品总数: n=%d\n",n);
printf("最大价值: c[%d][%d]=%d\n",n,m,c[n][m]);

for(int i=0; i<n; i++)
printf("物品价值: p[%d]=%d\n",i,p[i]);


return c;
}


int* Print_Pack_01(int** c, int w[], int m, int n)
{
// int x[] = new int[n];
int* x=(int*)malloc((n)*sizeof(int));
for(int i=0; i<n; i++)
{
x[i]=0;
}

for(int i=n; i>0; i--) //从最后一个状态记录c[n][m]开始逆推 
{
//如果c[i][m]大于c[i-1][m],说明c[i][m]这个最优值中包含了w[i-1](注意这里是i-1,因为c数组长度是n+1)  
if(c[i][m]>c[i-1][m])
{
x[i-1]=1;
m-=w[i-1];
}
}

for(int i=0; i<n; i++)
{
if(x[i]==0)
{
printf("x[%d]=%d; w[%d]=%d; 没有 装入背包\n",i,x[i],i,w[i]); //x[i]=1表示该物品被装入背包,x[i]=0示该物品没有被装入背包
}else
{
printf("x[%d]=%d; w[%d]=%d; 已经 装入背包\n",i,x[i],i,w[i]); //x[i]=1表示该物品被装入背包,x[i]=0示该物品没有被装入背包
}
}
return x;
}  

==========运行结果如下=====================

0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 40 40 40 40 40 40
0 10 10 10 10 40 50 50 50 50 50
0 10 10 10 25 40 50 50 50 65 75
0 10 10 30 40 40 50 55 70 80 80


=======
背包容量: m=10
物品总数: n=4
最大价值: c[4][10]=80
物品价值: p[0]=40
物品价值: p[1]=10
物品价值: p[2]=25
物品价值: p[3]=30
x[0]=1; w[0]=5; 已经 装入背包
x[1]=1; w[1]=1; 已经 装入背包
x[2]=0; w[2]=4; 没有 装入背包
x[3]=1; w[3]=3; 已经 装入背包
请按任意键继续. . .

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值