油克小学期(附加题)动态规划0-1背包问题

(一)算法设计

1. 问题描述:

设容积为V的背包中有n个提及分别为ai(i=1,2,3,..n)的物体,价值分别为ci,如何确保装进包内的物体具有最大价值?

如V=10,有a,b,c,d,e物体,信息如下:

物品

a

b

c

d

e

体积

2

2

6

5

4

价值

6

3

5

4

6

如何确保装进包内的物体具有最大价值?求解装进包内哪些物体时价值最大?

2. 最优值过程分析:

背包的状态转换方程

f [ i, j ] = Max{ f [ i - 1, j - Wi ] + Pi( j >= Wi ) , f [ i - 1 , j ] }

f [ i, j ]表示:在前i件物品中选择若干件放在承重为j的背包中,可以取得的最大价值。

Pi表示:第i件物品的价值。

《算法图解》一书中给出计算公式:

name

weight

value

1

2

3

4

5

6

7

8

9

10

a

2

6

 0  

6

6

9

9

12

12

15

15

15

b

2

3

0

3

3

6

6

9

9

9

10

11

c

6

5

0

0

0

6

6

6

6

6

10

11

d

5

4

0

0

0

6

6

6

6

6

10

10

e

4

6

0

0

0

6

6

6

6

6

6

6

3. 简要分析:

这张表是至底向上,从左到右生成的。用e2表示e行2列的单元格,

这个单元格的意义是用来表示只有物品e时,有个承重为2的背包,那么这个背包的最大价值是0,因为e物品的重量是4,背包装不了。

对于d2单元格,表示只有物品e,d时,承重为2的背包,所能装入的最大价值,仍然是0,因为物品e,d都不是这个背包能装的。同理,c2=0,b2=3,a2=6。对于承重为8的背包,a8=15,考虑如何得出?

根据01背包的状态转换方程,需要考察两个值,一个是f [ i - 1 , j ],对于这个例子来说就是b8的值9,另一个是f [ i - 1, j- Wi ] + Pi;在这里,f [ i - 1 , j ]表示一个承重为8的背包,当只有物品b,c,d,e四件可选时这个背包能装入的最大价值,f [ i -1, j - Wi ]表示有一个承重为6的背包(等于当前背包承重减去物品a的重量),当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值,f [ i -1, j - Wi ]就是指单元格b6,值为9,Pi指a物品的价值,即6,由于f [ i - 1, j - Wi ] +Pi = 9 + 6 = 15 大于f [ i - 1,j ] = 9,所以物品a应该放入承重为8的背包。

(二)程序实现

1. 代码实现

``c
#include <iostream>
#include<cstdio>
#define N 100
#define MAX(a,b) a < b ? b : a
using namespace std;

struct goods{
int sign;//物品序号
int wight;//物品重量
int value;//物品价值
};

int n,bestValue,cv,cw,C;//物品数量,价值最大,当前价值,当前重量,背包容量
int X[N],cx[N];//最终存储状态,当前存储状态
struct goods goods[N];

int KnapSack(int n,struct goods a[],int C,int X[]){
    int V[N][10*N];
    for(int i = 0; i <= n; i++)//初始化第0列 
        V[i][0] = 0;
    for(int j = 0; j <= C; j++)//初始化第0行 
        V[0][j] = 0;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= C; j++)
        if(j < a[i-1].wight)
            V[i][j] = V[i-1][j];
        else
            V[i][j] = MAX(V[i-1][j],V[i-1][j-a[i-1].wight] + a[i-1].value);

    for(int i = n,j = C; i > 0; i--){
        if(V[i][j] > V[i-1][j]){
            X[i-1] = 1;
            j = j - a[i-1].wight;
        }
        else
            X[i-1] = 0;
    }
    return V[n][C];
}
int main()
{
    printf("物品种类n:");
    scanf("%d",&n);
    printf("背包容量C:");
    scanf("%d",&C);
    for(int i = 0; i < n; i++){
        printf("物品%d的重量w[%d]及其价值v[%d]:",i+1,i+1,i+1);
        scanf("%d%d",&goods[i].wight,&goods[i].value);
    }
    int sum2 = KnapSack(n,goods,C,X);
     printf("动态规划法求解0/1背包问题:\nX=[");
     for(int i = 0; i < n; i++)
        cout<<X[i]<<" ";//输出所求X[n]矩阵
     printf("]   装入总价值%d\n", sum2);
     return 0;
}

2. 运行结果

3. 构造求解过程表格

```c
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<queue>
#include<climits>
#include<cstring>
using namespace std;
const int c = 10;             //背包的容量
const int w[] = {0,2,2,6,5,4};//物品的重量,其中0号位置不使用 。 
const int v[] = {0,6,3,5,4,6};//物品对应的待加,0号位置置为空。
const int n = sizeof(w)/sizeof(w[0]) - 1 ; //n为物品的个数 
int x[n+1];
void package0_1(int m[][11],const int w[],const int v[],const int n)//n代表物品的个数 
{
    //采用从底到顶的顺序来设置m[i][j]的值
    //首先放w[n]
    for(int j = 0; j <= c; j++)
       if(j < w[n]) m[n][j] = 0;     //j小于w[n],所对应的值设为0,否则就为可以放置 
       else         m[n][j] = v[n];
       
    //对剩下的n-1个物品进行放置。
    int i;
    for(i = n-1; i >= 1; i--)
        for(int j = 0; j <= c; j++)
           if(j < w[i]) 
                        m[i][j] = m[i+1][j];//如果j < w[i]则,当前位置就不能放置,它等于上一个位置的值。
                                            //否则,就比较到底是放置之后的值大,还是不放置的值大,选择其中较大者。            
           else         m[i][j] = m[i+1][j] > m[i+1][j-w[i]] + v[i]? 
                                  m[i+1][j] : m[i+1][j-w[i]] + v[i];  
}
void answer(int m[][11],const int n)
{
    int j = c;
    int i;
    for(i = 1; i <= n-1; i++)
        if(m[i][j] == m[i+1][j]) x[i] = 0;
        else                    { 
                                 x[i] = 1;
                                 j = j - w[i];
                                 }    
    x[n] = m[i][j] ? 1 : 0; 
}
int main()
{
 int m[6][11]={0};
 
 package0_1(m,w,v,n);
 for(int i = 0; i <= 5; i++)
 {
     for(int j = 0; j <= 10; j++)
     printf("%2d  ",m[i][j]);
     cout << endl; 
 } 
 answer(m,n);
 cout << "The best answer is:\n";
 for(int i = 1; i <= 5; i++)
 cout << x[i] << " ";
 system("pause");
 return 0;
}

4. 运行结果

 

(三)参考资料:

0-1背包问题 - 简书 (jianshu.com)

(35条消息) 动态规划之01背包问题(最易理解的讲解)_从杰的博客-CSDN博客_动态规划01背包问题

(35条消息) 动态规划0—1背包问题___孤剑__独舞的博客-CSDN博客_0-1背包问题

(35条消息) 动态规划之0-1背包问题(详解+分析+原码)_未见花闻的博客-CSDN博客_动态规划背包问题算法分析

(35条消息) 0-1背包问题 【动态规划】_成就一亿技术人的博客-CSDN博客_0-1背包问题代码source)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北城学神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值