背包问题(dp初步)

来源:互联网 发布:青岛知豆在哪租的 编辑:程序博客网 时间:2024/05/21 11:03

问题一:0-1背包问题

有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?

如下表,利用动态规划思想从左到右,从上到下生成所有状态:
这里写图片描述

代码实现:

#include<cstdio>#include<iostream>#include<algorithm>using namespace std;const int N=5;//物品数目const int V=10;//背包容量int main(){    int weight[10]={-1,2,2,6,5,4};    int value[10]={-1,6,3,5,4,6};    int f[10][15]={0};    for(int i=1;i<=N;i++)    {        for(int j=0;j<=V;j++)        {            if(weight[i]<=j)                f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);            else                f[i][j]=f[i-1][j];        }     }    cout<<f[N][V]<<endl;    return 0;}

进一步思考,代码可以优化如下:

#include<cstdio>#include<iostream>#include<algorithm>using namespace std;const int N=5;//物品数目const int V=10;//背包容量int main(){    int weight[10]={-1,2,2,6,5,4};    int value[10]={-1,6,3,5,4,6};    int f[15]={0};    for(int i=1;i<=N;i++)    {        for(int j=V;j>=0;j--)        {            if(weight[i]<=j)                f[j]=max(f[j],f[j-weight[i]]+value[i]);        }    }    cout<<f[V]<<endl;    return 0;}

问题二:完全背包问题

完全背包问题:一个背包总容量为V,现在有N个物品,第i个 物品体积为weight[i],价值为value[i],每个物品都有无限多件,现在往背包里面装东西,怎么装能使背包的内物品价值最大?

状态转移方程为:
f[i+1][j]=max(f[i][j-k*weight[i+1]]+k*value[i+1]),其中0<=k<=V/weight[i+1]

代码实现:

#include<cstdio>#include<iostream>#include<algorithm>using namespace std;const int N=5;//物品数目const int V=10;//背包容量int main(){    int weight[10]={-1,2,2,6,5,4};    int value[10]={-1,6,3,5,4,6};    int f[15]={0};    for(int i=1;i<=N;i++)    {        for(int j=1;j<=V;j++)        {            if(weight[i]<=j)                f[j]=max(f[j],f[j-weight[i]]+value[i]);        }    }    cout<<f[V]<<endl;    return 0;}

问题三:多重背包问题

有一个容量为V的背包和N件物品,第i件物品最多有Num[i]件,每件物品的重量是weight[i],收益是value[i]。在不超过背包容量的情况下,最多能获得多少价值或收益

多重背包二进制拆分实现:

跟完全背包一样的道理,利用二进制的思想将n[i]件物品i拆分成若干件物品,目的是在0-n[i]中的任何数字都能用这若干件物品代换,另外,超过n[i]件的策略是不允许的。方法是将物品i分成若干件,其中每一件物品都有一个系数,这件物品的费用和价值都是原来的费用和价值乘以这个系数,使得这些系数分别为1,2,4,…,2^(k-1),n[i]-2^k+1,且k满足n[i]-2^k+1>0的最大整数。例如,n[i]=13,就将该物品拆成系数为1、2、4、6的四件物品。分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示。

状态转移方程:
dp[i][v]=max{ dp[i-1][v-k*c[i]]+k*w[i] }, 0<=k<=num[i]

代码实现:

#include<iostream>using namespace std;const int N=3;const int V=8;int weight[N+1]={-1,1,2,2};int value[N+1]={-1,6,10,20};int num[N+1]={-1,10,5,2};int f[V+1]={0};void _01pack(int _weight,int _value){    for(int v=V;v>=_weight;v--)        f[v]=max(f[v],f[v-_weight]+_value);}void completepack(int _weight,int _value){    for(int v=_weight;v<=V;v++)        f[v]=max(f[v],f[v-_weight]+_value);}int multipack(){    int k=1;    int coun=0;    for(int i=1;i<=N;i++)    {        if(weight[i]*num[i]>=V)            completepack(weight[i],value[i]);        else        {            k=1;            coun=num[i];            while(k<=coun)            {                _01pack(k*weight[i],k*value[i]);                coun-=k;                k*=2;            }            _01pack(coun*weight[i],coun*value[i]);        }    }    return f[V];}int main(){    cout<<multipack()<<endl;    return 0;}
原创粉丝点击