动态规划--背包问题(01、完全、多重)

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

 

    原文作者:动态规划
    原文地址: https://blog.csdn.net/qq_39667655/article/details/82357161
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞