当前位置:首页 > 技术 > 正文内容

多维背包问题中的内存优化与路径还原技术

访客 技术 2026年6月29日 1

本题为典型的多维背包问题,要求在满足多个资源限制条件下最大化价值,并输出选取物品的具体方案。由于状态维度较高(四维容量+一维物品),直接使用五维数组进行动态规划极易导致内存超限,因此需要采用数据类型优化或滚动数组等技巧来降低空间消耗。

题目输入包含 n 个物品,每个物品具有五项属性:四种资源消耗量 p, a, c, m 和对应的价值 g。目标是在给定的四种资源上限 P, A, C, M 下,选择若干物品使得总价值最大,并按编号升序输出所选物品的索引(从0开始)。

方法一:short 类型优化的五维 DP

考虑到单个状态值不会过大,可将 dp 数组定义为 short 类型以节省空间。同时使用 vis 数组记录转移路径。状态转移方程如下:

dp[i][j][k][l][t] = max(dp[i-1][j][k][l][t], dp[i-1][j-p[i]][k-a[i]][l-c[i]][t-m[i]] + g[i])

若更新发生,则标记 vis[i][j][k][l][t] = true。最终从最大价值状态反向追踪 vis 标记,还原出选取序列。

代码实现(short 优化版)

#include <bits/stdc++.h>
using namespace std;

typedef short s;
s n, P, A, C, M;
s p[37], a[37], c[37], m[37], g[37];
s dp[37][37][37][37][37];
bool vis[37][37][37][37][37];

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> p[i] >> a[i] >> c[i] >> m[i] >> g[i];
    cin >> P >> A >> C >> M;

    for (int i = 1; i <= n; ++i)
        for (int j = 0; j <= P; ++j)
            for (int k = 0; k <= A; ++k)
                for (int l = 0; l <= C; ++l)
                    for (int t = 0; t <= M; ++t) {
                        dp[i][j][k][l][t] = dp[i-1][j][k][l][t];
                        if (j >= p[i] && k >= a[i] && l >= c[i] && t >= m[i]) {
                            s val = dp[i-1][j-p[i]][k-a[i]][l-c[i]][t-m[i]] + g[i];
                            if (val > dp[i][j][k][l][t]) {
                                dp[i][j][k][l][t] = val;
                                vis[i][j][k][l][t] = true;
                            }
                        }
                    }

    s best_j = 0, best_k = 0, best_l = 0, best_t = 0;
    s max_val = 0;
    for (int j = 0; j <= P; ++j)
        for (int k = 0; k <= A; ++k)
            for (int l = 0; l <= C; ++l)
                for (int t = 0; t <= M; ++t)
                    if (dp[n][j][k][l][t] > max_val) {
                        max_val = dp[n][j][k][l][t];
                        best_j = j; best_k = k; best_l = l; best_t = t;
                    }

    vector<int> path;
    for (int i = n; i >= 1; --i) {
        if (vis[i][best_j][best_k][best_l][best_t]) {
            path.push_back(i - 1);
            best_j -= p[i];
            best_k -= a[i];
            best_l -= c[i];
            best_t -= m[i];
        }
    }

    cout << path.size() << endl;
    for (int i = path.size() - 1; i >= 0; --i)
        cout << path[i] << " ";
    cout << endl;

    return 0;
}

方法二:滚动数组优化空间

利用完全背包的倒序遍历思想,将第一维压缩掉,仅保留四维状态数组。此时需额外保存完整的 vis 矩阵用于路径回溯。状态转移改为从高容量向低容量迭代,避免重复使用同一物品。

代码实现(滚动数组版)

#include <bits/stdc++.h>
using namespace std;

short n, P, A, C, M;
short p[37], a[37], c[37], m[37], g[37];
short dp[37][37][37][37];
bool used[37][37][37][37][37]; // used[i][j][k][l][o]: 第i个物品是否在状态(j,k,l,o)中被选用

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> p[i] >> a[i] >> c[i] >> m[i] >> g[i];
    cin >> P >> A >> C >> M;

    memset(dp, 0, sizeof(dp));
    memset(used, 0, sizeof(used));

    for (int i = 1; i <= n; ++i)
        for (int j = P; j >= p[i]; --j)
            for (int k = A; k >= a[i]; --k)
                for (int l = C; l >= c[i]; --l)
                    for (int o = M; o >= m[i]; --o) {
                        short prev = dp[j - p[i]][k - a[i]][l - c[i]][o - m[i]];
                        if (prev + g[i] >= dp[j][k][l][o]) {
                            dp[j][k][l][o] = prev + g[i];
                            used[i][j][k][l][o] = true;
                        }
                    }

    vector<int> selected;
    int cur_p = P, cur_a = A, cur_c = C, cur_m = M;
    for (int i = n; i >= 1; --i) {
        if (used[i][cur_p][cur_a][cur_c][cur_m]) {
            selected.push_back(i - 1);
            cur_p -= p[i];
            cur_a -= a[i];
            cur_c -= c[i];
            cur_m -= m[i];
        }
    }

    cout << selected.size() << endl;
    for (int i = selected.size() - 1; i >= 0; --i)
        cout << selected[i] << " ";
    cout << endl;

    return 0;
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。