多重背包例题:庆功会

多重背包例题:庆功会

朴素版,二进制优化,单调队列优化三种写法。

原题链接:https://www.acwing.com/problem/content/1021/

补充

我的二进制优化博客

彩色铅笔大佬的多重背包III(单调队列优化)博客

dp思路

多重背包dp

dp 
状态表示
    集合:f[i][j]表示从前i种物品中选,价格不超过j的所有方案的集合
    属性:max 最大价值
状态转移
    [0,1,2,...,s[i]]
    
    0 <= k <= s[i]
    f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i])

代码

普通多重背包

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 510,M = 6010;
int v[N],w[N],s[N];
int n,m;
int f[N][M];

int main()
{
    scanf("%d%d",&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 = 1; j <= m;j ++)
        {
            for(int k = 0; k * v[i] <= j && k <= s[i]; k ++) // 这里k*v[i] <= j && k<=s[i]两个条件容易写出错
                f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
        }
    }
    cout << f[n][m];
    
    return 0;
}

二进制优化多重背包

#include<iostream>
#include<algorithm>
using namespace std;
const int M = 60100;
int v[M],w[M],cnt;
int n,m;
int f[M];

int main()
{
    scanf("%d%d",&n,&m);
    
    for(int i = 1;i <= n;i ++)
    {
        int a,b,s; // 物品体积 价值 数量
        scanf("%d%d%d",&a,&b,&s);
        // 将这s个物品拆开成logs组(二进制优化法):1 2 4 8 16... ...
        int k = 1;
        while(k < s)
        {
            cnt ++;
            v[cnt] = k * a;
            w[cnt] = k * b;
            s -= k; // 记得从s中减去k
            k *= 2;
        }
        if(s > 0)
        {
            cnt ++;
            v[cnt] = s * a;
            w[cnt] = s * b;
        }
    }
    
    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];
    
    return 0;
}

单调队列优化多重背包

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 510,M = 6010;

int v[N],w[N],s[N];
int f[M],g[M];
int q[M]; // 队列长度一定要开成体积大小(j的范围此题中是价格的范围)
int n,m;

int main()
{
    scanf("%d%d",&n,&m);
    
    for(int i = 1;i <= n;i ++) cin >> v[i] >> w[i] >> s[i];
    

    for(int i = 1;i <= n;i ++)
    {
        memcpy(g,f,sizeof g);
        for(int r = 0; r < v[i]; r ++)
        {
            int head = 0,tail = -1;
            for(int j = r; j <= m;j += v[i])
            {
                // 判断队头是否滑出
                if(head <= tail && q[head] < j-s[i]*v[i]) head ++;
                // 保证模拟队列递减(队头为最大值)
                while(head <= tail && g[q[tail]]+(j-q[tail])/v[i]*w[i] <= g[j]) tail --;
                q[++ tail] = j;
                f[j] = max(f[j],g[q[head]] + (j-q[head])/v[i]*w[i]);
            }
        }
    }
    printf("%d",f[m]);
    return 0;
}
posted @ 2022-10-28 21:18  r涤生  阅读(25)  评论(0编辑  收藏  举报