贪心算法应用:多重背包启发式疑问详解

news/2025/9/22 18:32:25/文章来源:https://www.cnblogs.com/lxjshuju/p/19105806

在这里插入图片描述

贪心算法应用:多重背包启发式问题详解

多重背包问题是经典的组合优化问题,也是贪心算法的重要应用场景。本文将全面深入地探讨Java中如何利用贪心算法解决多重背包问题。

多重背包问题定义

**多重背包问题(Multiple Knapsack Problem)**是背包问题的变种,描述如下:

与0-1背包(每种物品只能选0或1个)和完全背包(每种物品无限)不同,多重背包中物品有数量限制。

问题分析

数学表达

最大化:Σ(v_i * x_i) 对于i=1到n
约束条件:

  1. Σ(w_i * x_i) ≤ W 对于i=1到n
  2. 0 ≤ x_i ≤ c_i 且 x_i为整数

关键特性

  1. 组合爆炸:可能的组合数量为Π(c_i+1),直接枚举不可行
  2. NP难问题:没有已知的多项式时间解法
  3. 贪心可行:虽然不能保证最优解,但能得到近似解

贪心算法解决方案

1. 基本贪心策略

三种常见的贪心策略:

  1. 价值优先:按价值降序选择
  2. 重量优先:按重量升序选择
  3. 价值密度优先:按价值/重量(v_i/w_i)降序选择

实践证明,价值密度优先策略通常效果最好。

2. Java实现基础版

import java.util.Arrays;
import java.util.Comparator;
class Item
{
int weight;
int value;
int count;
public Item(int weight, int value, int count) {
this.weight = weight;
this.value = value;
this.count = count;
}
}
public class MultipleKnapsack
{
public static int greedySolution(Item[] items, int capacity) {
// 按价值密度排序
Arrays.sort(items, new Comparator<
Item>() {
@Override
public int compare(Item a, Item b) {
double densityA = (double)a.value / a.weight;
double densityB = (double)b.value / b.weight;
return Double.compare(densityB, densityA);
// 降序
}
});
int totalValue = 0;
int remainingCapacity = capacity;
for (Item item : items) {
if (remainingCapacity <= 0) break;
// 计算可以取多少个当前物品
int maxTake = Math.min(item.count, remainingCapacity / item.weight);
if (maxTake >
0) {
totalValue += maxTake * item.value;
remainingCapacity -= maxTake * item.weight;
}
}
return totalValue;
}
public static void main(String[] args) {
Item[] items = {
new Item(2, 10, 3),
new Item(3, 5, 2),
new Item(5, 15, 2),
new Item(7, 7, 3),
new Item(1, 6, 4)
};
int capacity = 15;
System.out.println("最大价值: " + greedySolution(items, capacity));
}
}

3. 算法复杂度分析

  1. 排序:O(n log n)
  2. 贪心选择:O(n)
    总时间复杂度:O(n log n)

空间复杂度:O(1)(不包括输入存储)

改进的贪心算法

基础贪心算法可能不是最优的,我们可以通过以下方法改进:

1. 贪心+动态规划混合

public static int hybridSolution(Item[] items, int capacity) {
// 先按贪心算法得到一个解
int greedyValue = greedySolution(items, capacity);
// 对高价值物品尝试动态规划
Arrays.sort(items, (a, b) -> b.value - a.value);
int n = items.length;
int[] dp = new int[capacity + 1];
for (int i = 0; i < n; i++) {
Item item = items[i];
for (int k = 1; k <= item.count; k++) {
for (int w = capacity; w >= k * item.weight; w--) {
dp[w] = Math.max(dp[w], dp[w - k * item.weight] + k * item.value);
}
}
}
return Math.max(greedyValue, dp[capacity]);
}

2. 多阶段贪心选择

public static int multiPhaseGreedy(Item[] items, int capacity) {
// 阶段1:价值密度优先
int phase1 = greedySolution(items, capacity);
// 阶段2:纯价值优先
Arrays.sort(items, (a, b) -> b.value - a.value);
int phase2 = greedySolution(items, capacity);
// 阶段3:纯重量优先
Arrays.sort(items, (a, b) -> a.weight - b.weight);
int phase3 = greedySolution(items, capacity);
return Math.max(phase1, Math.max(phase2, phase3));
}

完全解与贪心解对比

动态规划解法(最优解)

public static int dpSolution(Item[] items, int capacity) {
int[] dp = new int[capacity + 1];
for (Item item : items) {
for (int w = capacity; w >= 0; w--) {
for (int k = 1; k <= item.count; k++) {
if (w >= k * item.weight) {
dp[w] = Math.max(dp[w], dp[w - k * item.weight] + k * item.value);
}
}
}
}
return dp[capacity];
}

对比分析

方法时间复杂度空间复杂度解的质量
纯贪心O(n log n)O(1)近似
动态规划O(nWC_max)O(W)最优
贪心+动态规划混合O(n log n + nWC_limited)O(W)较好

其中C_max是最大物品数量,C_limited是限制的高价值物品数量

性能优化技巧

  1. 物品预处理

  2. 搜索剪枝

    public static int greedyWithPruning(Item[] items, int capacity) {
    Arrays.sort(items, (a, b) ->
    Double.compare((double)b.value/b.weight, (double)a.value/a.weight));
    int[] best = {
    0
    };
    backtrack(items, 0, capacity, 0, best);
    return best[0];
    }
    private static void backtrack(Item[] items, int index, int remaining, int currentValue, int[] best) {
    if (index == items.length || remaining == 0) {
    if (currentValue > best[0]) best[0] = currentValue;
    return;
    }
    Item item = items[index];
    int maxTake = Math.min(item.count, remaining / item.weight);
    // 从最多开始尝试,贪心顺序
    for (int k = maxTake; k >= 0 &&
    (maxTake - k) <= 100; k--) {
    if (remaining - k * item.weight >= 0) {
    // 剪枝:如果剩余容量全部用下一个物品也无法超越当前最优
    double upperBound = currentValue + k * item.value;
    if (index + 1 < items.length) {
    upperBound += (remaining - k * item.weight) *
    ((double)items[index+1].value/items[index+1].weight);
    }
    if (upperBound <= best[0]) break;
    backtrack(items, index + 1, remaining - k * item.weight,
    currentValue + k * item.value, best);
    }
    }
    }
  3. 并行处理

    public static int parallelGreedy(Item[] items, int capacity) {
    // 多种贪心策略并行执行
    int[] results = new int[3];
    Thread t1 = new Thread(() ->
    {
    Arrays.sort(items, (a, b) ->
    Double.compare((double)b.value/b.weight, (double)a.value/a.weight));
    results[0] = greedySolution(items, capacity);
    });
    Thread t2 = new Thread(() ->
    {
    Arrays.sort(items, (a, b) -> b.value - a.value);
    results[1] = greedySolution(items, capacity);
    });
    Thread t3 = new Thread(() ->
    {
    Arrays.sort(items, (a, b) -> a.weight - b.weight);
    results[2] = greedySolution(items, capacity);
    });
    t1.start(); t2.start(); t3.start();
    try { t1.join(); t2.join(); t3.join();
    } catch (InterruptedException e) {
    }
    return Math.max(results[0], Math.max(results[1], results[2]));
    }

实际应用场景

多重背包问题在现实中有广泛应用:

  1. 资源分配:服务器资源分配,选择最有价值的任务组合
  2. 投资组合:在有限资金下选择不同数量的多种投资产品
  3. 生产计划:原材料切割,最大化利用原材料
  4. 广告投放:在有限广告位中选择不同频次的广告组合
  5. 运输装载:货车装载多种货物,考虑数量限制

变种问题与扩展

1. 多维多重背包

每种物品有多个维度的重量限制(如体积和重量):

class MultiDimensionalItem
{
int[] weights;
// 各维度的重量
int value;
int count;
}
public static int multiDimensionalGreedy(MultiDimensionalItem[] items, int[] capacities) {
// 按综合价值密度排序
Arrays.sort(items, (a, b) ->
{
double densityA = a.value / Arrays.stream(a.weights).sum();
double densityB = b.value / Arrays.stream(b.weights).sum();
return Double.compare(densityB, densityA);
});
int[] remaining = Arrays.copyOf(capacities, capacities.length);
int totalValue = 0;
for (MultiDimensionalItem item : items) {
boolean canTakeMore = true;
while (canTakeMore) {
// 检查是否可以再取一个当前物品
for (int i = 0; i < remaining.length; i++) {
if (remaining[i] < item.weights[i]) {
canTakeMore = false;
break;
}
}
if (canTakeMore && item.count >
0) {
totalValue += item.value;
item.count--;
for (int i = 0; i < remaining.length; i++) {
remaining[i] -= item.weights[i];
}
} else {
break;
}
}
}
return totalValue;
}

2. 分组多重背包

物品分为若干组,每组只能选择一定数量的物品:

class Group
{
Item[] items;
int maxSelect;
// 该组最多选择的物品数
}
public static int groupKnapsack(Group[] groups, int capacity) {
// 两层贪心:先对组排序,再对组内物品排序
Arrays.sort(groups, (a, b) ->
{
double maxDensityA = Arrays.stream(a.items)
.mapToDouble(item ->
(double)item.value/item.weight)
.max().orElse(0);
double maxDensityB = Arrays.stream(b.items)
.mapToDouble(item ->
(double)item.value/item.weight)
.max().orElse(0);
return Double.compare(maxDensityB, maxDensityA);
});
int remaining = capacity;
int totalValue = 0;
for (Group group : groups) {
Arrays.sort(group.items, (a, b) ->
Double.compare((double)b.value/b.weight, (double)a.value/a.weight));
int groupSelected = 0;
for (Item item : group.items) {
if (groupSelected >= group.maxSelect) break;
if (remaining <= 0) break;
int maxTake = Math.min(item.count, remaining / item.weight);
maxTake = Math.min(maxTake, group.maxSelect - groupSelected);
if (maxTake >
0) {
totalValue += maxTake * item.value;
remaining -= maxTake * item.weight;
groupSelected += maxTake;
}
}
}
return totalValue;
}

测试与验证

测试用例设计

应包含以下类型测试用例:

  1. 基础用例:简单验证功能
  2. 边界用例:空输入、单个物品、容量为0等
  3. 性能用例:大量物品测试算法效率
  4. 极端用例:所有物品重量相同、价值相同等特殊情况

JUnit测试示例

import org.junit.Test;
import static org.junit.Assert.*;
public class MultipleKnapsackTest
{
@Test
public void testEmptyItems() {
Item[] items = {
};
assertEquals(0, MultipleKnapsack.greedySolution(items, 10));
}
@Test
public void testZeroCapacity() {
Item[] items = {
new Item(2, 10, 3),
new Item(3, 5, 2)
};
assertEquals(0, MultipleKnapsack.greedySolution(items, 0));
}
@Test
public void testSingleItemWithinCapacity() {
Item[] items = {
new Item(5, 20, 2)
};
assertEquals(40, MultipleKnapsack.greedySolution(items, 10));
}
@Test
public void testSingleItemExceedCapacity() {
Item[] items = {
new Item(15, 30, 3)
};
assertEquals(0, MultipleKnapsack.greedySolution(items, 10));
}
@Test
public void testMixedItems1() {
Item[] items = {
new Item(2, 10, 3),
new Item(3, 5, 2),
new Item(5, 15, 2),
new Item(7, 7, 3),
new Item(1, 6, 4)
};
// 贪心解:3个重量1价值6 + 3个重量2价值10 = 3*6 + 3*10 = 48
assertEquals(48, MultipleKnapsack.greedySolution(items, 15));
}
@Test
public void testMixedItems2() {
Item[] items = {
new Item(10, 60, 1),
new Item(20, 100, 1),
new Item(30, 120, 1)
};
// 最优解是取重量20和30的物品
assertEquals(220, MultipleKnapsack.hybridSolution(items, 50));
}
@Test
public void testLargeInput() {
// 生成100个随机物品测试性能
Item[] items = new Item[100];
for (int i = 0; i <
100; i++) {
int w = 1 + (int)(Math.random() * 10);
int v = 1 + (int)(Math.random() * 20);
int c = 1 + (int)(Math.random() * 5);
items[i] = new Item(w, v, c);
}
int capacity = 100;
long start = System.nanoTime();
int result = MultipleKnapsack.greedySolution(items, capacity);
long end = System.nanoTime();
System.out.println("Large test result: " + result);
System.out.println("Time taken: " + (end - start)/1e6 + " ms");
assertTrue(result >
0);
}
}

算法选择指南

在实际应用中如何选择算法:

  1. 小规模问题(n < 100,W < 1000):使用动态规划获取精确解
  2. 中规模问题(100 < n < 10^4):使用贪心+动态规划混合方法
  3. 大规模问题(n > 10^4):使用纯贪心算法或并行贪心
  4. 实时系统:必须使用纯贪心算法保证响应时间
  5. 离线处理:可以使用更复杂的混合方法

常见问题与解决

  1. 贪心解与最优解差距大

  2. 性能瓶颈

  3. 整数溢出

  4. 浮点精度问题

总结

多重背包问题是组合优化中的经典问题,贪心算法提供了高效的近似解决方案。Java实现时需要注意:

  1. 物品排序策略的选择
  2. 贪心与动态规划的平衡
  3. 性能与解质量的权衡
  4. 各种边界条件的处理

通过合理选择和改进贪心策略,可以在大多数实际应用中获得满意的结果。对于需要精确解的小规模问题,可以结合动态规划;对于大规模问题,贪心算法是唯一可行的选择。

关键点总结:

  • 价值密度优先的贪心策略通常效果最好
  • 混合方法可以平衡效率和质量
  • 预处理和剪枝能显著提高性能
  • 测试要充分,特别是边界情况

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/909983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中标公示查询网站网络营销服务外包

加速时如何换挡&#xff0c;您知道吗?为了使换挡过程顺利进行&#xff0c;变速器内齿轮平稳啮合&#xff0c;必须掌握好发动机转速&#xff0c;在适当时机推动变速杆操纵齿轮啮合。为此&#xff0c;要通过反复练习&#xff0c;一边踩踏油门踏板&#xff0c;一边听发动机运转声…

划重点|云栖大会「AI 原生应用架构论坛」看点梳理

AI 潮正以不可阻挡之势重塑千行百业。兴奋与喧嚣过后,如何将强大的 AI 能力,真正高效、可靠地融入企业业务、从“可用”走向“好用”等问题是所有企业和技术人必须解决的问题。 答案正在指向一个全新的范式——AI 原…

君子如水,心中有火:vivo本心而为30周年

「 水之灵的战术应变,水之韧的战略坚守,水之谦的价值观底色 」 不同时代、不同背景下的企业,有着不同的生存哲学。 在过去三十年中国企业迅猛发展的历程中,中国很多科技企业大都是在强调竞争,把对手干掉,最终赢家…

Margin 塌陷问题如何解决?触发BFC。BFC的概念和触发条件

1️⃣ 什么是 Margin 塌陷 【现象】两个垂直方向相邻的块级盒子(兄弟或父子)之间的margin 会合并为其中的最大值,而不是两者相加。【影响】兄弟元素:上下margin合并为其中的最大值  父子元素:如果父元素没有paddi…

网站开发目的与意义wordpress 您没有足够的权限访问该页面.

如果需要一个全局对象&#xff0c;如对话框、系统日志、显卡等设备的驱动程序对象、一台PC连接一个键盘等。这样的全局对象只能是一个且是全局的&#xff0c;这就是单例模式&#xff0c;如何实现呢&#xff1f;1 不能在类外部通过构造函数新建对象&#xff1a;构造函数的访问方…

返利网站怎么做做我的狗在什么网站上看

以下内容摘自笔者即将出版的最新著作《深入理解计算机网络》一书。本书将于12月底出版上市&#xff0c;敬请留意&#xff01;&#xff01; 本书原始目录参见此文&#xff1a;http://winda.blog.51cto.com/55153/1063878 5.3.2 循环冗余校验检错方案 上节介绍的奇偶校验码&#…

哪些网站做品牌折扣的建站公司技术服务费

在开发工程中线程可以帮助我们提高运行速度&#xff0c;Android开发中我知道的线程有四个一个是老生长谈的Thread&#xff0c;第二个是asyncTask,第三个&#xff1a;TimetTask,第四个是Looper,四个多线程各有个的有点&#xff0c;Thread的运行速度是最快的&#xff0c;AsyncTas…

什么企业做网站比较方便呢微信注册小程序步骤

1&#xff0c; 概述 1.1 课题背景 本系统由说书客面向广大民营药店、县区级医院、个体诊所等群体的药品和客户等信息的管理需求&#xff0c;采用SpringSpringMVCMybatisEasyui架构实现&#xff0c;为单体药店、批发企业、零售连锁企业&#xff0c;提供有针对性的信息数据管理…

邵东网站开发微信小程序h5开发

flex如何做响应式设计Responsive design is not just about the web that automatically adjusts to different screen resolutions and resizeable images, but designs that are crucial for web performance.自适应设计不仅涉及可自动适应不同屏幕分辨率和可调整大小图像的网…

郑州门户网站建设哪家好有了自己的域名怎么做网站

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着互联网技术的不断…

广州建站模板诚信网站平台建设方案

在无限的整数序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...中找到第 n 个数字。 注意: n 是正数且在32为整形范围内 ( n < 231)。 示例 1: 输入: 3 输出: 3 示例 2: 输入: 11 输出: 0 说明: 第11个数字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... 里是0&#xff0c;它是…

伊犁州住房城乡建设局网站建站快车加盟

铁路订票管理系统按照权限的类型进行划分&#xff0c;分为用户和管理员两个模块。管理员模块主要针对整个系统的管理进行设计&#xff0c;提高了管理的效率和标准。主要功能包括个人中心、用户管理、火车类型管理、火车信息管理、车票预订管理、车票退票管理、系统管理等&#…

手机影视素材网站大全汽油价格92号最新调整时间

1. 指针和数组 C语言中只有一维数组&#xff0c;而且数组的大小必须在编译器就作为一个常数确定下来&#xff0c;然而在C语言中数组的元素可以是任何类型的对象&#xff0c;当然也可以是另外的一个数组&#xff0c;这样&#xff0c;要仿真出一个多维数组就不是难事。 对于一个…

app和网站哪个难做宁夏免费建个人网站

物品名称物品代码电池battery.small骨头碎片bone.fragments空的豆罐头can.beans.empty空的金枪鱼罐头can.tuna.empty摄像头cctv.camera木炭charcoal煤coal石油crude.oil炸药explosives动物脂肪fat.animal火药gunpowder高级金属矿hq.metal.ore金属碎片metal.fragments金属矿meta…

商家自己做的商品信息查询网站网站开发结论

本文实例讲述了PHP双向链表定义与用法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;由于需要对一组数据多次进行移动操作&#xff0c;所以写个双向链表。但对php实在不熟悉&#xff0c;虽然测试各个方法没啥问题&#xff0c;就是不知道php语言深层的这些指针和unset…

漯河市万金镇网站建设建设个人网站用到的技术

matlab语言丰富的图形表现方法&#xff0c;使得数学计算结果可以方便地、多样性地实现了可视化&#xff0c;这是其它语言所不能比拟的。;第一节 符号函数绘图第二节 图形编辑第三节 2D数据图第四节 3D数据图第五节 MATLAB的视图功能第六节 图像、视频和声音;plot —— 最基本的…

嘉兴品牌网站初学者求教怎样做网站

序言 Sentinel 是阿里巴巴开源的一款流量防护与监控平台&#xff0c;它可以帮助开发者有效地管理微服务的流量&#xff0c;实现流量控制、熔断降级、系统负载保护等功能。本文将介绍如何在项目中部署和配置 Sentinel 控制台&#xff0c;实现微服务的流量防护和监控。 一、Sen…

深圳网站开发的公司网站建设与管理案例...

智能优化算法应用&#xff1a;基于适应度相关算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于适应度相关算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.适应度相关算法4.实验参数设定5.算法…

长春做网站公司企业名录搜索软件 2022

上一篇文章已经介绍了线程的基本概念以及线程相关的API&#xff0c;下面来看一下线程池 一、线程池框架 1、线程池的优点 重用线程池中的线程&#xff0c;避免因为线程的创建和销毁所带来的性能开销。 能有效控制线程池的最大并发数&#xff0c;避免大量线程之间因互相抢夺系…

可以登录国外网站吗宿州城乡建设局网站

转载自 JAVA面试常考系列三 题目一 什么是迭代器(Iterator)&#xff1f; 迭代器&#xff08;iterator&#xff09;是一种对象&#xff0c;它能够用来遍历标准模板库容器中的部分或全部元素&#xff0c;每个迭代器对象代表容器中确定的地址。迭代器提供了一种方法&#xff0c;可…