关于背包问题的总结

背包问题的分类:
1. 01背包问题
2. 完全背包问题
3. 多重背包问题
4. 完全背包问题

DP问题的解题思路:
关于背包问题的总结_第1张图片

  1. 01背包问题
    问题描述:见例题:01背包问题
    问题分析:对于每一个物品,可以选择要也可以选择。所以状态的计算就是更新i所表示的集合,因此,f(i,j) = max(f[i-1][j],f[i-1][j-v[i]]+w[i])。这就是朴素版的01背包问题,代码如下:
#include
using namespace std;
const int N = 1010;
int n,m;
int v[N],w[N];
int f[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j = 0;j<=m;j++)
        {
            f[i][j] = f[i-1][j];//不选第i个物品
            if(v[i] <= j) f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]); //看看选了第i个是否更好
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

优化:我们可以发现i状态的f只与i-1有关,因此可以用滚动数组进行优化,只用一个一维数组就可以保存答案。此时j循环需要从m到v[i],因为j-v[i]的状态要在j状态更新之后。代码如下:

#include
using namespace std;
const int N = 1010;
int n,m;
int v[N],w[N];
int f[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j = m;j >= v[i];j--)
        {
            f[j] = max(f[j],f[j-v[i]]+w[i]); 
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

2.完全背包问题 完全背包问题
问题分析:对于每一个物品,可以不选择,也可以选择任意多个(所选体积需要小于V)
朴素版代码如下:(会超时)

#include
using namespace std;
const int N = 1010;
int v[N],w[N];
int n,m;
int f[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            for(int k = 0;k*v[i] <= j;k++)
            {
                f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
            }
        }
    }
    cout<<f[n][m]<<"\n";
    return 0;
}

优化如下:
f[i][j] = max(f[i-1][j],f[i-1][j-v]+w,f[i-1][j-2v]+2w,…f[i-1][j-kv]+k*w)
f[i[j-v] = max( f[i-1][j-v], f[i-1][j-2v]+w,f[i-1][j-3v]+2w,…f[i-1][j-kv]+(k-1)w)
第一个在第二个式子上面加上一个w。
所以f[i][j]可以优化为max(f[i][j],f[i][j-v]+w);
代码如下:

#include
using namespace std;
const int N = 1010;
int v[N],w[N];
int n,m;
int f[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            f[i][j] = f[i-1][j];
            if(j >= v[i]) f[i][j] = max(f[i][j],f[i][j-v[i]]+w[i]);
        }
    }
    cout<<f[n][m]<<"\n";
    return 0;
}

最后按照上面的更新方法,根据滚动数组,优化为一维的。
因为这里的状态更新是根据本层来更新的,所以不需要逆序。

#include
using namespace std;
const int N = 1010;
int v[N],w[N];
int n,m;
int f[N];
int main()
{
    cin>>n>>m;
    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<=m;j++)
        {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[m]<<"\n";
    return 0;
}

3.多重背包问题 多重背包I
问题分析,这里合完全背包区别在于数量不是无限的,因此,稍加修改即可

#include
using namespace std;
const int N = 110;
int n,m;
int v[N],w[N],s[N];
int f[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i]>>s[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            for(int k = 0;k<=s[i]&&k*v[i]<=j;k++)
            {
                f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
            }
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

多重背包优化 多重背包问题II
这里使用二进制来进行优化,每一种物品都可以用1,2,4,8…捆绑表示,所以可以将此问题转化为01背包问题,物品是捆绑之后的物品。这里直接写最后代码:

#include
using namespace std;
const int N = 1010,M = 25000;
int n,m;
int v[M],w[M];
int f[M];
int main()
{
    cin>>n>>m;
    int cnt = 0;//计算捆绑的编号
    for(int i = 1;i<=n;i++)
    {
        int a,b,s;
        cin>>a>>b>>s;
        int k = 1;
        while(k <= s)
        {
            cnt++;
            v[cnt] = k*a;
            w[cnt] = k*b;
            s -= k;
            k*=2;
        }
        if(s) {cnt++;v[cnt] = s*a,w[cnt] = s*b;}
    }
    n=cnt;
    for(int i=1;i<=n;i++)
    {
        for(int j=m;j>=v[i];j--)
        {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

4.分组背包问题:分组背包
朴素版:

#include
using namespace std;
const int N = 110;
int n,m;
int v[N][N],w[N][N],s[N];
int f[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        for(int j=0;j<s[i];j++)
        {
            cin>>v[i][j]>>w[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j =0;j<=m;j++)
        {
            f[i][j] = f[i-1][j];
            for(int k=0;k<s[i];k++)
            {
                if(v[i][k] <= j) f[i][j] = max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
            }
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

按照刚刚的方法进行优化

#include
using namespace std;
const int N = 110;
int n,m;
int v[N][N],w[N][N],s[N];
int f[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        for(int j=0;j<s[i];j++)
        {
            cin>>v[i][j]>>w[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j =m;j>=0;j--)
        {
            for(int k=0;k<s[i];k++)
            {
                if(v[i][k] <= j) f[j] = max(f[j],f[j-v[i][k]]+w[i][k]);
            }
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

背包问题到此结束
参考 : ACWing基础课

你可能感兴趣的:(c++,动态规划,算法)