首页 > 其他分享 >多重背包、混合背包

多重背包、混合背包

时间:2024-10-21 13:10:07浏览次数:1  
标签:vector 多重 cnt 背包 int value 混合 cost dp

多重背包、混合背包

P1776 宝物筛选

一共有n种货物, 背包容量为 t
每种货物的价值(v[i])、重量(w[i])、数量(c[i])都给出
请返回选择货物不超过背包容量的情况下,能得到的最大的价值

多重背包不进行枚举优化

  • 严格位置依赖的动态规划
#include <iostream>
#include <vector>

using namespace std;

// 时间复杂度 O(n * w * 每种商品的平均个数)
int main() {
    int n, w;
    cin >> n >> w;
    vector<int> cost(n + 1);
    vector<int> value(n + 1);
    vector<int> cnt(n + 1);
    for (int i = 1; i <= n; ++i)
        cin >> value[i] >> cost[i] >> cnt[i];

    // dp[i][j] 表示前 i 号物品,在每种物品不超过限制,且总重量也不超过 j 的情况下,获得的最大价值
    vector<vector<int>> dp(n + 1, vector<int>(w + 1));
    // 表示没有货物的情况下,背包容量不管是多少,最大价值都是 0
    fill(dp[0].begin(), dp[0].end(), 0);
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j <= w; ++j) {
            // 一个都不选
            dp[i][j] = dp[i - 1][j];
            // 选若干个,不超过每种物品的限制
            for (int k = 1; k <= cnt[i] && j - k * cost[i] >= 0; k++)
                dp[i][j] = max(dp[i][j], dp[i - 1][j - k * cost[i]] + k * value[i]);
        }
    }
    cout << dp[n][w];
}
  • 空间压缩
#include <iostream>
#include <vector>

using namespace std;

// 时间复杂度 O(n * w * 每种商品的平均个数)
int main() {
    int n, w;
    cin >> n >> w;
    vector<int> cost(n + 1);
    vector<int> value(n + 1);
    vector<int> cnt(n + 1);
    for (int i = 1; i <= n; ++i)
        cin >> value[i] >> cost[i] >> cnt[i];

    // dp[i][j] 表示前 i 号物品,在每种物品不超过限制,且总重量也不超过 j 的情况下,获得的最大价值
    // 首行全 0
    vector<int> dp(w + 1, 0);
    for (int i = 1; i <= n; ++i) {
        for (int j = w; j >= 0; j--) {
            // 一个都不选,或者选若干个,但不超过每种物品的限制
            for (int k = 1; k <= cnt[i] && j - k * cost[i] >= 0; k++)
                dp[j] = max(dp[j], dp[j - k * cost[i]] + k * value[i]);
        }
    }
    cout << dp[w];
}

多重背包通过二进制分组转化成 01 背包(模版)

#include <iostream>
#include <vector>

using namespace std;

// 时间复杂度 O(t * (log(第 1 种商品的个数) + log(第 2 种商品的个数) + ... + log(第 n 种商品的个数)))
int main() {
    // w 为背包容量
    int n, w;
    cin >> n >> w;
    // 衍生出的物品总数
    int m = 0;
    vector<int> value(1);
    vector<int> cost(1);
    for (int i = 1, _value, _cost, _cnt; i <= n; ++i) {
        cin >> _value >> _cost >> _cnt;
        // 二进制分组:每组放 2^(k-1) 个当前物品,时间复杂度为 O(log(_cnt))
        for (int k = 1; k <= _cnt; k <<= 1) {
            value.emplace_back(k * _value);
            cost.emplace_back(k * _cost);
            _cnt -= k;
            m++;
        }
        // 最后剩下的归为一组
        if (_cnt > 0) {
            value.emplace_back(_cnt * _value);
            cost.emplace_back(_cnt * _cost);
            m++;
        }
    }

    // 01 背包空间压缩
    vector<int> dp(w + 1, 0);
    for (int i = 1; i <= m; ++i)
        for (int j = w; j >= cost[i]; j--)
            dp[j] = max(dp[j], dp[j - cost[i]] + value[i]);
    cout << dp[w];
}

多重背包单调队列优化

  • 严格位置依赖的动态规划
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

// 当前来到 i 号货物,需要 j 位置的指标,返回指标值
int getValue(vector<vector<int>> &dp, vector<int> &cost, vector<int> &value, int i, int j) {
    return dp[i - 1][j] - (j / cost[i]) * value[i];
}

// 时间复杂度 O(n * w)
int main() {
    int n, w;
    cin >> n >> w;
    vector<int> cost(n + 1);
    vector<int> value(n + 1);
    vector<int> cnt(n + 1);
    for (int i = 1; i <= n; ++i)
        cin >> value[i] >> cost[i] >> cnt[i];

    // dp[i][j] 表示前 i 号物品,在每种物品不超过限制,且总重量也不超过 j 的情况下,获得的最大价值
    vector<vector<int>> dp(n + 1, vector<int>(w + 1));
    // 表示没有货物的情况下,背包容量不管是多少,最大价值都是 0
    fill(dp[0].begin(), dp[0].end(), 0);
    // 单调队列,存放列号
    deque<int> q;
    for (int i = 1; i <= n; ++i) {
        // 同余分组
        for (int mod = 0; mod <= min(w, cost[i] - 1); mod++) {
            q.clear();
            for (int j = mod; j <= w; j += cost[i]) {
                // 弹出收益不如当前位置的列号
                while (!q.empty() && getValue(dp, cost, value, i, q.back()) <= getValue(dp, cost, value, i, j))
                    q.pop_back();
                q.emplace_back(j);
                // 单调队列头部过期
                if (q.front() == j - cost[i] * (cnt[i] + 1))
                    q.pop_front();
                dp[i][j] = getValue(dp, cost, value, i, q.front()) + (j / cost[i]) * value[i];
            }
        }
    }
    cout << dp[n][w];
}
  • 空间压缩
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

// 当前来到 i 号货物,需要 j 位置的指标,返回指标值
int getValue(vector<int> &dp, vector<int> &cost, vector<int> &value, int i, int j) {
    return dp[j] - (j / cost[i]) * value[i];
}

// 时间复杂度 O(n * w)
int main() {
    int n, w;
    cin >> n >> w;
    vector<int> cost(n + 1);
    vector<int> value(n + 1);
    vector<int> cnt(n + 1);
    for (int i = 1; i <= n; ++i)
        cin >> value[i] >> cost[i] >> cnt[i];

    // dp[i][j] 表示前 i 号物品,在每种物品不超过限制,且总重量也不超过 j 的情况下,获得的最大价值
    // 首行全 0
    vector<int> dp(w + 1, 0);
    // 单调队列,存放列号
    deque<int> q;
    for (int i = 1; i <= n; ++i) {
        for (int mod = 0; mod <= min(w, cost[i] - 1); mod++) {
            q.clear();
            // 先把 cnt[i] 个的指标进入单调队列
            for (int j = w - mod, count = 1; j >= 0 && count <= cnt[i]; j -= cost[i], count++) {
                while (!q.empty() && getValue(dp, cost, value, i, q.back()) <= getValue(dp, cost, value, i, j))
                    q.pop_back();
                q.emplace_back(j);
            }
            for (int j = w - mod, enter = j - cost[i] * cnt[i]; j >= 0; j -= cost[i], enter -= cost[i]) {
                // 窗口进入 enter 位置的指标
                if (enter >= 0) {
                    while (!q.empty()
                           && getValue(dp, cost, value, i, q.back()) <= getValue(dp, cost, value, i, enter))
                        q.pop_back();
                    q.emplace_back(enter);
                }
                dp[j] = getValue(dp, cost, value, i, q.front()) + (j / cost[i]) * value[i];
                if (q.front() == j) q.pop_front();
            }
        }
    }
    cout << dp[w];
}

P1833 樱花

#include <iostream>
#include <vector>

using namespace std;

int main() {
    string time1, time2;
    cin >> time1 >> time2;
    int h1 = stoi(time1.substr(0, time1.find(':')));
    int m1 = stoi(time1.substr(time1.find(':') + 1, time1.size()));
    int h2 = stoi(time2.substr(0, time2.find(':')));
    int m2 = stoi(time2.substr(time2.find(':') + 1, time2.size()));
    // w 为背包容量
    int w = (h2 - h1) * 60 + (m2 - m1);

    // 物品种类数
    int n;
    cin >> n;
    // 衍生出的物品总数
    int m = 0;
    vector<int> value(1);
    vector<int> cost(1);
    for (int i = 1, _value, _cost, _cnt; i <= n; ++i) {
        cin >> _cost >> _value >> _cnt;
        // 可以看无数遍,但实际有时间限制,w 时间内,即使每种树只要一分钟,那最多也就看 w 遍
        if (_cnt == 0) _cnt = w;
        // 二进制分组:每组放 2^(k-1) 个当前物品,时间复杂度为 O(log(_cnt))
        for (int k = 1; k <= _cnt; k <<= 1) {
            value.emplace_back(k * _value);
            cost.emplace_back(k * _cost);
            _cnt -= k;
            m++;
        }
        // 最后剩下的归为一组
        if (_cnt > 0) {
            value.emplace_back(_cnt * _value);
            cost.emplace_back(_cnt * _cost);
            m++;
        }
    }

    // 01 背包空间压缩
    vector<int> dp(w + 1, 0);
    for (int i = 1; i <= m; ++i)
        for (int j = w; j >= cost[i]; j--)
            dp[j] = max(dp[j], dp[j - cost[i]] + value[i]);
    cout << dp[w];
}

混合背包 + 多重背包普通窗口优化

混合背包 + 多重背包普通窗口优化

能成功找零的钱数种类
每一种货币都给定面值val[i],和拥有的数量cnt[i]
想知道目前拥有的货币,在钱数为1、2、3...m时
能找零成功的钱数有多少
也就是说当钱数的范围是1~m
返回这个范围上有多少可以找零成功的钱数

#include <iostream>

using namespace std;

const int MAX_N = 101;
const int MAX_M = 100001;
// n 为硬币总数,m 为背包容量
int n, m;
// 硬币面额
int value[MAX_N];
// 硬币个数
int cnt[MAX_N];
// dp[i][j] 表示前 i 种硬币,在数量不超过限制的情况下,能否刚好凑出 j
bool dp[MAX_M];

int compute() {
    for (int i = 1; i <= m; ++i) dp[i] = false;
    dp[0] = true;
    for (int i = 1; i <= n; ++i) {
        if (cnt[i] == 1) {
            // 当前硬币只有一个
            // 01 背包的空间压缩实现是从右往左更新
            for (int j = m; j >= value[i]; j--)
                if (dp[j - value[i]])
                    dp[j] = true;
        } else if (value[i] * cnt[i] > m) {
            // 这种硬币的总值超过背包容量
            // 完全背包的空间压缩实现是从左往右更新
            for (int j = value[i]; j <= m; ++j)
                if (dp[j - value[i]])
                    dp[j] = true;
        } else {
            // 多重背包的空间压缩实现
            // 每一组都是从右往左更新
            // 同余分组
            for (int mod = 0; mod < value[i]; mod++) {
                int trueCnt = 0;
                for (int j = m - mod, size = 0; j >= 0 && size <= cnt[i]; j -= value[i], size++)
                    trueCnt += dp[j] ? 1 : 0;
                for (int j = m - mod, l = j - value[i] * (cnt[i] + 1); j >= 1; j -= value[i], l -= value[i]) {
                    if (dp[j]) {
                        trueCnt--;
                    } else {
                        if (trueCnt != 0) dp[j] = true;
                    }
                    if (l >= 0) trueCnt += dp[l] ? 1 : 0;
                }
            }
        }
    }
    int res = 0;
    for (int i = 1; i <= m; i++)
        if (dp[i]) res++;
    return res;
}

int main() {
    cin >> n >> m;
    while (n != 0 || m != 0) {
        for (int i = 1; i <= n; ++i) cin >> value[i];
        for (int i = 1; i <= n; ++i) cin >> cnt[i];
        cout << compute() << endl;
        cin >> n >> m;
    }
}

标签:vector,多重,cnt,背包,int,value,混合,cost,dp
From: https://www.cnblogs.com/sprinining/p/18489244

相关文章

  • MoH:融合混合专家机制的高效多头注意力模型及其在视觉语言任务中的应用
    在深度学习领域,多头注意力机制一直是Transformer模型的核心组成部分,在自然语言处理和计算机视觉任务中取得了巨大成功。然而,研究表明并非所有的注意力头都具有同等重要性,许多注意力头可以在不影响模型精度的情况下被剪枝。基于这一洞察,这篇论文提出了一种名为混合头注意力(Mi......
  • VASCO:增减材混合制造的体积和表面共分解
    ......
  • 01背包、有依赖的背包
    01背包、有依赖的背包P1048[NOIP2005普及组]采药01背包(模版)给定一个正数t,表示背包的容量有m个货物,每个货物可以选择一次每个货物有自己的体积costs[i]和价值values[i]返回在不超过总容量的情况下,怎么挑选货物能达到价值最大返回最大的价值二维dp数组#incl......
  • 背包
    01背包简单,话不多说,上代码完全背包每个物品有无限个,做法就是把容量的循环倒过来。例题,其实这道题重点考察不是背包,是集合和数学证明。多重背包详解,重点看看二进制优化吧。分组背包例题,就是每组组内枚举,做\(01\)背包。......
  • 【初窥算法】动态规划之背包问题
    背包问题概述背包问题(KnapsackProblem)是计算机科学和运筹学中的一个经典问题,通常描述为:给定一组物品,每种物品都有自己的重量和价值,在限定的最大承重(背包容量)下,如何选择物品使得背包内物品的总价值最大。背包问题有多种表现形式,包括但不限于:0/1背包问题:有n种物品,每种物品......
  • TPAMI 2024 | 用于点云分割领域适应的组合语义混合
    题目:CompositionalSemanticMixforDomainAdaptationinPointCloudSegmentation用于点云分割领域适应的组合语义混合作者:CristianoSaltori;FabioGalasso;GiuseppeFiameni;NicuSebe;FabioPoiesi;ElisaRicci源码链接:https://github.com/saltoricristiano......
  • [独家原创]基于深度混合核极限学习机(DHKELM)的数据多输出回归预测 Matlab (多输入多
    [独家原创]基于深度混合核极限学习机(DHKELM)的数据多输出回归预测(多输入多输出)每个输出都有以下线性拟合图等四张图!!!具体看图,独家图像!!!程序已经调试好,替换数据集根据输出个数修改outdim值即可运行!!!数据格式为excel!(如下)你先用你就是创新!!!1.也可以定制添加优化算法!2.将多项......
  • PHP与C#混合用
    故事背景是这样的,有一套项目,服务器端是用C#写的,为了完成某种事情,它需要使用到一个组件,这个组件很小但很重要,很不巧的是,这个这个组件是用PHP语言写的,如果为了使用这个组件而专门搭建一个PHP的环境显得有点高射炮打蚊子(况且还有其他不可预见的阻力)。或许有读者会提出“抗议”:不是PHP......
  • Exchange 2016与国内版O365混合部署(4):配置Exchange 公网证书
    Exchange2016与国内版O365混合部署(4):配置Exchange公网证书Exchange混合部署需要安装一个第三方权威机构颁发的公网证书,用于邮件流的安全传输,详细的步骤technet也有写:https://technet.microsoft.com/zh-cn/library/mt595782(v=exchg.150).aspx 实验中我们需要先在服务器上创......
  • Exchange 2016与国内版O365混合部署(3):安装Exchange2016并配置邮件的外网收发
    Exchange2016与国内版O365混合部署(3):安装Exchange2016并配置邮件的外网收发Exchange2016安装和内网邮件收发测试:Exchange2016的安装这块这里就不做详细介绍了,网上也有很多教程,并不复杂。安装完成后登录https://localhost/ecp管理员中心页面查看:(略)登录https://localhost/owa:......