【背包问题】多重背包及其优化

多重背包I

题目描述

在这里插入图片描述

朴素版代码(时间复杂度:O(NVS))

#include <iostream>

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++) // 限制1:数量,限制2:体积
                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 <iostream>

using namespace std;

const int N = 110;

int n,m;
int f[N][N];

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

利用滚动数组优化掉一维

#include <iostream>

using namespace std;

const int N = 110;

int n,m;
int v[N],w[N],s[N];
int f[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=m;j>=0;j--)
            for(int k=0;k<=s[i] && k * v[i] <=j;k++)
                f[j] = max(f[j],f[j - k * v[i]] + k * w[i]);
    
    cout<<f[m]<<endl;
    
    return 0;
}

多重背包问题 II(二进制优化,由此看出多重背包也是特殊的01背包)

题目描述(注意数据范围已扩大到1000)

在这里插入图片描述

代码(时间复杂度:O(NVlog(S))

#include <iostream>

using namespace std;

const int N = 12010, M = 2010; // N = 2000 * log(2000)
// 核心:利用二进制把s份拆成 log(s) 份

int n,m;
int v[N], w[N];
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] = a * k;
            w[cnt] = b * k;
            s -= k;
            k *= 2;
        }
        if(s > 0) // 剩下的凑成一组
        {
    
    
            cnt ++ ;
            v[cnt] = a * s;
            w[cnt] = b * s;
        }
    }
    
    n = cnt; // 总共有cnt组,做一遍01背包
    
    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;
}

简洁版(边读入边更新,和滚动数组优化)

#include <iostream>

using namespace std;

const int N = 12010, M = 2010; // N = 2000 * log(2000)
// 核心:利用二进制把s份拆成 log(s) 份

int n,m;
int f[M];

int main()
{
    
    
    cin >> n >> m;
    
    for(int i=0;i<n;i++)
    {
    
    
        int v,w,s;
        cin >> v >> w >> s;
        
        for(int k = 1;k <= s;k *= 2)
        {
    
    
         	for(int j = m;j >= k * v; j -- )
             	f[j] = max(f[j], f[j - k * v] + k * w);
         	s -= k;
        }
        
        if(s)
        {
    
    
            for(int j = m;j >= s * v;j --)
                f[j] = max(f[j],f[j-s * v] + s * w);
        }
    }
    
    cout<<f[m]<<endl;
    
    return 0;
}

多重背包问题 III

题目描述(注意数据范围)

在这里插入图片描述

单调队列优化(时间复杂度:O(NV))

在这里插入图片描述

代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 20010;

int n,m;
int f[N],g[N],q[N];

int main()
{
    
    
    cin >> n >> m;
    for(int i=0;i<n;i++)
    {
    
    
        int v,w,s;
        cin >> v >> w >> s;
        memcpy(g,f,sizeof f);
        for(int j =0;j<v;j++) // 这里的j是在枚举 j % v的余数,范围从0~v-1
        {
    
    
            int hh=0,tt=-1;
            for(int k =j;k<=m;k+=v)
            {
    
    
                if(hh <= tt && q[hh] < k - s * v) hh ++;
                if(hh <= tt) f[k] = max(f[k],g[q[hh]] + (k - q[hh]) / v * w);
                while(hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w) tt--;
                q[++ tt] = k;
            }
        }
    }
    
    cout<<f[m]<<endl;
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43154149/article/details/106219413#comments_20633979