背包问题总结

1.01背包
有n种物品,一个容量为c的背包,每种物品的体积是v,重量是w,且每种物品只有一件,可以选择放或者不放。求那些物品放入背包可以让背包重量最大。

初始化问题:这里有两种情况1)恰好装满:这时就只有背包容量为0的可以被0个物品恰好装满,所以容量为零的背包初始化为0,其余的均不符合合法状态所以初始化为负无穷大。2)不要求恰好装满:这时每个容量的背包都有什么都不装的合法解所以初始化为0。

例题:http://codevs.cn/problem/5709/
有两种方式:
1)

#include<iostream>
#include<string.h>
using namespace std;

const int MAX=1000+10;
int dp[MAX][MAX];
int main()
{
    int n,c,v,w;
    memset(dp,0,sizeof(dp));
    cin>>c>>n;
    for(int i=1; i<=n;i++)
    {
        cin>>v>>w;
        for(int j=1;j<=c;j++)
        {
            if(j>=v)
            {
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w);
            }
            else
            {
                dp[i][j]=dp[i-1][j];
            }
        }
    }
    cout<<dp[n][c]<<endl;
} 

这里dp[i][j]数组的i表示前几种物品,j表示背包容量
2)

#include<iostream>
using namespace std;

const int MAX = 1000+10;
int dp[MAX];

int main()
{
    int n,c,v,w;
    cin>>c>>n;
    for(int i=0; i<n;i++)
    {
        cin>>v>>w;
        for(int j=c; j>=v;j--)
        {
            dp[j]=max(dp[j],dp[j-v]+w);
        }
    }
    cout<<dp[c]<<endl;
    return 0;
}

这里将二维数组变为一维进行空间优化,注意的是,需要逆序推,这样之前的数组元素就是上一个状态的。

两种方式中第二重循环是核心,是01背包的子问题。
总结一个01背包核心函数:

void ZeroOne_Pack(int volume,int weight,int n)  
{  
    for(int i=n; i>=volume; i--)  
        dp[i] = max(dp[i],dp[i-volume] + weight);  
} 

2.完全背包
有N种物品和一个容量为c的背包,每种物品都有无限件可用。
第i种物品的体积是v,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

完全背包与01背包类似,不同的是01背包选择放或不放,而完全背包是选择放多少个,完全背包与01背包只是推的顺序不一样。01背包之所以逆序推是因为第i次循环是由第i-1次推出的,从而保证考虑选择第i个物品是依据之前未选择i这个物品的子问题。而完全背包恰好相反,它可能要依据之前选了i物品的子问题。
核心函数:

void Complete_Pack(int volume,int weight,int n)  
{  
    for(int i=volume; i<=n; i++)  
        dp[i] = max(dp[i],dp[i-volume] + weight);  
} 

3.多重背包
有N种物品和一个容量为c的背包。第i种物品最多有n件可用,每件体积是v,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。
这里用了一个定理:
一个正整数n可以被分解成1,2,4,…,2^(k-1),n-2^k+1(k是满足n-2^k+1>0的最大整数)的形式,且1~n之内的所有整数均可以唯一表示成1,2,4,…,2^(k-1),n-2^k+1中某几个数的和的形式。

int Multiple_Pack(int v[],int w[],int c[],int n,int m) //v是体积,w是重量,c是数量,n是物品种类,m是背包容量 
{  
    memset(dp,0,sizeof(dp));  
    for(int i=1; i<=n; i++)  
    {  
        if(c[i]*v[i] > m)  
            Complete_Pack(v[i],w[i],m);  
        else  
        {  
            int k = 1;  
            while(k < c[i])  
            {  
                ZeroOne_Pack(k*v[i],k*w[i],m);  
                c[i] -= k;  
                k <<= 1;  
            }  
            ZeroOne_Pack(c[i]*v[i],c[i]*w[i],m);  
        }  
    }  
    return dp[m];  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值