背包问题:01背包与完全背包

一、介绍

背包问题是最广为人知的动态规划问题,都是给定限定的背包容量与物品,求所能装下的最大价值:

完全背包:

有n种物品,每种均有无限多,第i种物品额体积为v[i],重量(价值)为w[i]。

01背包:

有n种物品,每种只有一个,第i种物品额体积为v[i],重量(价值)为w[i]。

在背包问题中,我们把不同种的物品设为不同的阶段,即有n各阶段。


二、状态定义

定义状态d[i][j],表示把前i种物品装到容量为j的背包中的最大价值。

每次操作的对象是单个物品,那么对于物品有装入或者不装两种选择。

扫描二维码关注公众号,回复: 1116499 查看本文章


注:

示例输入:

n=3,V=9,v1=2,w1=2,v2=2,w2=2,v3=5,w3=6

三、完全背包

之所以叫完全背包,是因为每种物品均有无限多。

状态转移:

d[i][j]=max{d[i-1][j],d[i][j-v[i]]+w[i]}

max种第一项表示不装第i种,跳过此种;第二项表示装入此种,判定还留在此种。

普通实现:

#include <iostream>
using namespace std;
int n,V,v[50],w[50],d[50][500];
int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++)
        cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=V;j++){
        d[i][j]=d[i-1][j];
        if(j>=v[i])
        d[i][j]=max(d[i][j],d[i][j-v[i]]+w[i]);
        }
    cout<<d[n][V]<<endl;
    return 0;
}

注:在循环中首先要先进行:d[i][j]=d[i-1][j]而不直接进行max操作,是因为若j<v[i],d[i][j]将为0。

运行程序可得结果为10,即选择了两个第一种物品与一个第三种物品。

滚动数组实现:

#include <iostream>
using namespace std;
int n,V,v[50],w[50],d[50];
int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++)
        cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=v[i];j<=V;j++)
            d[j]=max(d[j],d[j-v[i]]+w[i]);
    cout<<d[V]<<endl;
    return 0;
}


四、01背包

因为需要对每个物品进行放或不放两种状态,故为01背包。

状态转移:

因为每种物品只有一个,所以对于每种的判定也变成了每个物品的判定。

d[i][j]=max{d[i-1][j],d[i-1][j-v[i]]+w[i]}

不管放入或不放入都跳到下一种的判定。

普通实现:

#include <iostream>
using namespace std;
int n,V,v[50],w[50],d[50][500];
int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++)
        cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=V;j++){
        d[i][j]=d[i-1][j];
        if(j>=v[i])
        d[i][j]=max(d[i][j],d[i-1][j-v[i]]+w[i]);
        }
    cout<<d[n][V]<<endl;
    return 0;
}
运行结果为9,即放入了第二种与第三种

滚动数组:

#include <iostream>
using namespace std;
int n,V,v[50],w[50],d[50];
int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++)
        cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=V;j>=v[i];j--)
            d[j]=max(d[j],d[j-v[i]]+w[i]);
    cout<<d[V]<<endl;
    return 0;
}


五、两种背包滚动数组实现对比

可以看01与完全背包在滚动数组中,只在内层循环次序不同,这是因为它们上一个状态不同导致,应回溯到其本质的状态转移方程。

对于存在两层循环的数组d[i][j],若i递增,那么对于其滚动数组d[j],原数组与滚动数组的对应关系为:

d[j]->d[i-1][j]

对于正数k:

j递增:d[j-k]->d[i][j-k];d[j+k]->d[i-1][j+k]

j递减:d[j-k]->d[i-1][j-k];d[j+k]->d[i][j+k]

猜你喜欢

转载自blog.csdn.net/a493823882/article/details/79593180