当前位置: 首页 > article >正文

算法【多重背包】

多重背包是指每一种物品给定数量的限制,进行可能性展开。对于多重背包的枚举优化,本文介绍最常用的二进制分组优化,其他优化如单调队列优化可自行学习。

下面通过题目加深理解。

题目一

测试链接:宝物筛选 - 洛谷

分析:这道题就是典型的多重背包模板,对于多重背包可能性的展开也十分容易想到,对于第i个物品不取或者依次取1个、2个、3个等。这样就可以写出记忆化搜索的版本,代码如下。

#include <iostream>
using namespace std;
int n, W;
int treasure[100][3];
int dp[100][40001];
int f(int index, int weight){
    if(index == n){
        return 0;
    }
    if(dp[index][weight] != -1){
        return dp[index][weight];
    }
    int ans = f(index+1, weight);
    for(int i = 1;i <= treasure[index][2] && weight - i * treasure[index][1] >= 0;++i){
        ans = ans > f(index+1, weight - i * treasure[index][1]) + i * treasure[index][0] ?
        ans : f(index+1, weight - i * treasure[index][1]) + i * treasure[index][0];
    }
    dp[index][weight] = ans;
    return ans;
}
int main(void){
    scanf("%d%d", &n, &W);
    for(int i = 0;i < n;++i){
        scanf("%d%d%d", &treasure[i][0], &treasure[i][1], &treasure[i][2]);
    }
    for(int i = 0;i < n;++i){
        for(int j = 0;j <= W;++j){
            dp[i][j] = -1;
        }
    }
    printf("%d", f(0, W));
    return 0;
}

其中,f方法返回在从下标为index物品开始取,剩余重量为weight的情况下取得的最大值。不过这道题目对于时间复杂度要求比较高,记忆化搜索的版本过不去。所以可以写出严格位置依赖的版本。代码如下。

#include <iostream>
using namespace std;
int n, W;
int treasure[100][3];
int dp[101][40001];
int main(void){
    scanf("%d%d", &n, &W);
    for(int i = 0;i < n;++i){
        scanf("%d%d%d", &treasure[i][0], &treasure[i][1], &treasure[i][2]);
    }
    for(int i = 0;i <= W;++i){
        dp[n][i] = 0;
    }
    for(int i = 0;i < n;++i){
        dp[i][0] = 0;
    }
    for(int i = n-1;i >= 0;--i){
        for(int j = 0;j <= W;++j){
            dp[i][j] = dp[i+1][j];
            for(int k = 1;k <= treasure[i][2] && j - k * treasure[i][1] >= 0;++k){
                dp[i][j] = dp[i][j] > dp[i+1][j-k*treasure[i][1]] + k * treasure[i][0] ?
                dp[i][j] : dp[i+1][j-k*treasure[i][1]] + k * treasure[i][0];
            }
        }
    }
    printf("%d", dp[0][W]);
    return 0;
}

其中,dp数组的含义和记忆化搜索中f方法的含义基本相同,因为严格位置依赖的版本就是根据记忆化搜索写出来的。不过,严格位置依赖的版本依旧过不去。下面还可以写出空间压缩的版本。代码如下。

#include <iostream>
using namespace std;
int n, W;
int treasure[100][3];
int dp[40001];
int main(void){
    scanf("%d%d", &n, &W);
    for(int i = 0;i < n;++i){
        scanf("%d%d%d", &treasure[i][0], &treasure[i][1], &treasure[i][2]);
    }
    for(int i = 0;i <= W;++i){
        dp[i] = 0;
    }
    for(int i = n-1;i >= 0;--i){
        for(int j = W;j >= 0;--j){
            for(int k = 1;k <= treasure[i][2] && j - k * treasure[i][1] >= 0;++k){
                dp[j] = dp[j] > dp[j-k*treasure[i][1]] + k * treasure[i][0] ?
                dp[j] : dp[j-k*treasure[i][1]] + k * treasure[i][0];
            }
        }
    }
    printf("%d", dp[W]);
    return 0;
}

虽然空间压缩相对于严格位置依赖不会有明显的时间复杂度优化,不过对于这道题目空间压缩还是压着线过了。对于多重背包的真正的优化在题目二体现。

题目二

测试链接:宝物筛选 - 洛谷

分析:对于多重背包一般的优化是将多重背包转化为01背包进行求解。就是将多重背包的件数转化为多个二进制背包的形式,这样可以通过转化出的多个物品不同的取舍方案得到对多重背包取不同件数的体现。比如件数为7,可以从1开始,转化为1,2,4三个背包,对于多重背包7件取多少件,可以通过1,2,4这三个背包取或不取得到。代码如下。

#include <iostream>
using namespace std;
int n, W;
int treasure[2000][2];
int dp[40001];
int main(void){
    int v_i, w_i, m_i, index = 0, temp;
    scanf("%d%d", &n, &W);
    for(int i = 0;i < n;++i){
        scanf("%d%d%d", &v_i, &w_i, &m_i);
        temp = 1;
        while (m_i >= temp)
        {
            treasure[index][0] = temp * v_i;
            treasure[index++][1] = temp * w_i;
            m_i -= temp;
            temp *= 2;
        }
        if(m_i != 0){
            treasure[index][0] = m_i * v_i;
            treasure[index++][1] = m_i * w_i;
        }
    }
    for(int i = 0;i <= W;++i){
        dp[i] = 0;
    }
    for(int i = 0;i < index;++i){
        for(int j = W;j >= 0 && j - treasure[i][1] >= 0;--j){
            dp[j] = dp[j] > dp[j-treasure[i][1]] + treasure[i][0] ?
            dp[j] : dp[j-treasure[i][1]] + treasure[i][0];
        }
    }
    printf("%d", dp[W]);
    return 0;
}

题目三

测试链接:樱花 - 洛谷

分析:这道题看似有一个完全背包,但是可以将完全背包转化为多重背包。即如果次数表示无数次,但是要在满足时间条件的情况下,所以求出一个最大次数和原无数次是等价的,这样就转化为了多重背包。代码如下。

#include <iostream>
using namespace std;
int Time, n;
int tree[1000000][2];
int dp[1001];
int main(void){
    int hour1, hour2, minute1, minute2;
    char ch;
    int T_i, C_i, P_i, temp, index = 0;
    cin>>hour1>>ch>>minute1>>hour2>>ch>>minute2;
    Time = (hour2 - hour1) * 60 + minute2 - minute1;
    cin>>n;
    for(int i = 0;i < n;++i){
        scanf("%d%d%d", &T_i, &C_i, &P_i);
        temp = 1;
        if(P_i == 0){
            P_i = Time / T_i;
        }
        while (P_i >= temp)
        {
            tree[index][0] = temp * T_i;
            tree[index++][1] = temp * C_i;
            P_i -= temp;
            temp *= 2;
        }
        if(P_i > 0){
            tree[index][0] = P_i * T_i;
            tree[index++][1] = P_i * C_i;
        }
    }
    for(int i = 0;i <= Time;++i){
        dp[i] = 0;
    }
    for(int i = 0;i < index;++i){
        for(int j = Time;j >= 0 && j - tree[i][0] >= 0;--j){
            dp[j] = dp[j] > dp[j-tree[i][0]] + tree[i][1] ?
            dp[j] : dp[j-tree[i][0]] + tree[i][1];
        }
    }
    printf("%d", dp[Time]);
    return 0;
}

其中,dp数组表示在下标0~i的物品取,剩余重量为j的情况下取得的最大值。


http://www.kler.cn/a/528390.html

相关文章:

  • Bash 基础与进阶实践指南
  • Signature
  • 单细胞分析基础-第一节 数据质控、降维聚类
  • 《DeepSeek R1:大模型最简安装秘籍》
  • 自定义数据集 使用pytorch框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测,对预测结果计算精确度和召回率及F1分数
  • SAP SD学习笔记28 - 请求计划(开票计划)之2 - Milestone请求(里程碑开票)
  • 【贪心算法篇】:“贪心”之旅--算法练习题中的智慧与策略(一)
  • 记7(激活函数+多层神经网络+梯度下降法及其优化
  • Unity3D仿星露谷物语开发26之创建场景控制管理器
  • 蓝桥杯刷题DAY1:前缀和
  • 项目练习:重写若依后端报错cannot be cast to com.xxx.model.LoginUser
  • C++ Primer 自定义数据结构
  • Linux-CentOS的yum源
  • 阶段一Python核心编程:走进Python编程的世界001
  • nth_element函数——C++快速选择函数
  • C语言:数组的介绍与使用
  • Excel 技巧23 - 在Excel中用切片器做出查询效果(★★★)
  • 4 [危机13小时追踪一场GitHub投毒事件]
  • javaEE-6.网络原理-http
  • Arduino可以做哪些有意思的项目
  • Java泛型深度解析(JDK23)
  • 牛客网第k小(详解)c++
  • 分布式微服务系统架构第90集:现代化金融核心系统
  • 深度学习之“缺失数据处理”
  • 青少年编程与数学 02-008 Pyhon语言编程基础 11课题、字典与循环语句
  • nginx目录结构和配置文件