贪心算法应用:集合划分问题详解

news2025/6/7 15:32:58

在这里插入图片描述

贪心算法与集合划分问题详解

集合划分问题是组合优化中的经典问题,其核心目标是将元素集合划分为若干满足特定条件的子集。本文将深入探讨贪心算法在集合划分中的应用,涵盖算法原理、适用场景、Java实现细节及优化策略。


一、集合划分问题定义

1.1 基础概念
给定一个集合 S = {x₁, x₂, ..., xₙ},需要将其划分为 k 个子集 {S₁, S₂, ..., Sₖ},满足:

  1. 完备性:所有子集的并为原集合
  2. 互斥性:任意两个子集交集为空
  3. 特定约束:如子集和相等、子集大小相近等

1.2 常见变种问题

  1. 等和划分:将集合划分为两个子集,使得两者和相等(如LeetCode 416)
  2. 最小最大子集和:划分至k个子集,使最大子集和最小(如LeetCode 698)
  3. 平衡划分:使子集元素数量或属性差异最小
  4. 多维度划分:同时考虑多个属性(如重量+体积)

1.3 应用场景

  • 服务器负载均衡
  • 分布式文件存储
  • 生产线任务调度
  • 数据处理分片

二、贪心算法策略设计

2.1 基本贪心策略
核心思想:通过局部最优选择逐步逼近全局最优解

通用步骤

  1. 排序预处理:按关键属性(如数值大小)排序
  2. 分配策略:依次将元素分配到当前最优子集
  3. 终止条件:所有元素分配完成

2.2 典型分配策略

问题类型排序方式分配策略
等和划分降序排序优先填充大元素
最小最大子集和降序排序当前总和最小的子集优先
平衡数量划分无需排序轮询分配

2.3 正确性分析

  • 等和划分:当总和为偶数且无超大元素时有效
  • 最小最大和:提供近似解,近似比通常为2
  • NP-Hard证明:多数划分问题属于NP-Hard,贪心提供可行近似解

三、等和划分问题详解

3.1 问题定义
给定非空数组 nums,判断是否能将其划分为两个子集,使得两个子集的和相等。

3.2 贪心算法实现

public class BalancedPartition {
    
    // 辅助类:记录子集状态
    static class Subset {
        int sum = 0;
        List<Integer> elements = new ArrayList<>();
    }

    public static boolean canPartition(int[] nums) {
        int total = Arrays.stream(nums).sum();
        if (total % 2 != 0) return false;
        
        int target = total / 2;
        Arrays.sort(nums); // 升序排序
        reverse(nums);     // 自定义降序
        
        Subset[] subsets = new Subset[2];
        subsets[0] = new Subset();
        subsets[1] = new Subset();
        
        for (int num : nums) {
            // 选择当前总和较小的子集
            int idx = (subsets[0].sum <= subsets[1].sum) ? 0 : 1;
            if (subsets[idx].sum + num > target) {
                // 无法放入则尝试另一个子集
                idx = 1 - idx;
                if (subsets[idx].sum + num > target) return false;
            }
            subsets[idx].sum += num;
            subsets[idx].elements.add(num);
        }
        return subsets[0].sum == subsets[1].sum;
    }
    
    private static void reverse(int[] arr) {
        int left = 0, right = arr.length - 1;
        while (left < right) {
            int temp = arr[left];
            arr[left++] = arr[right];
            arr[right--] = temp;
        }
    }

    public static void main(String[] args) {
        int[] nums1 = {1, 5, 11, 5};
        System.out.println(canPartition(nums1)); // true
        
        int[] nums2 = {1, 2, 3, 5};
        System.out.println(canPartition(nums2)); // false
    }
}

3.3 算法分析

  • 时间复杂度:O(n log n)(排序耗时)
  • 空间复杂度:O(n)(存储子集信息)
  • 局限性:无法处理存在单个元素超过总和一半的情况

四、最小最大子集和问题

4.1 问题定义
给定数组 nums 和整数 k,将其划分为 k 个连续非空子集,使得最大子集和最小。

4.2 贪心策略实现

public class MinMaxSubsetSum {
    
    public static int minMaxSum(int[] nums, int k) {
        Arrays.sort(nums);
        reverse(nums);
        
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        for (int i = 0; i < k; i++) minHeap.offer(0);
        
        for (int num : nums) {
            int curr = minHeap.poll();
            curr += num;
            minHeap.offer(curr);
        }
        
        int max = Integer.MIN_VALUE;
        while (!minHeap.isEmpty()) max = Math.max(max, minHeap.poll());
        return max;
    }
    
    private static void reverse(int[] arr) {
        // 同前文实现
    }

    public static void main(String[] args) {
        int[] nums = {7, 2, 5, 10, 8};
        System.out.println(minMaxSum(nums, 2)); // 输出18([7,2,5]和[10,8])
    }
}

4.3 关键逻辑解析

  1. 降序排序:优先处理大元素
  2. 最小堆维护子集和:每次选择当前和最小的子集
  3. 近似比证明:该策略结果不超过最优解的2倍

五、多维约束划分问题

5.1 问题描述
考虑元素的多个属性(如重量、体积、价值),需同时满足多个约束条件。

5.2 装箱问题变种

class Item {
    int weight;
    int volume;
    
    public Item(int w, int v) {
        weight = w;
        volume = v;
    }
}

public class MultiDimBinPacking {
    
    public static int minBins(Item[] items, int maxWeight, int maxVolume) {
        Arrays.sort(items, (a, b) -> 
            Integer.compare(b.weight + b.volume, a.weight + a.volume));
        
        List<Bin> bins = new ArrayList<>();
        
        for (Item item : items) {
            boolean placed = false;
            // 尝试放入已有箱子
            for (Bin bin : bins) {
                if (bin.canAdd(item, maxWeight, maxVolume)) {
                    bin.addItem(item);
                    placed = true;
                    break;
                }
            }
            // 创建新箱子
            if (!placed) {
                Bin newBin = new Bin();
                newBin.addItem(item);
                bins.add(newBin);
            }
        }
        return bins.size();
    }
    
    static class Bin {
        int currentWeight = 0;
        int currentVolume = 0;
        
        boolean canAdd(Item item, int maxW, int maxV) {
            return currentWeight + item.weight <= maxW 
                && currentVolume + item.volume <= maxV;
        }
        
        void addItem(Item item) {
            currentWeight += item.weight;
            currentVolume += item.volume;
        }
    }
}

5.3 策略分析

  • 复合排序:根据权重和体积的综合指标排序
  • 首次适应策略:遍历现有容器尝试放置
  • 复杂度:O(n²) 时间复杂度,适用于中小规模数据

六、性能优化技巧

6.1 数据结构优化
使用TreeSet加速查找:

TreeSet<Bin> bins = new TreeSet<>(Comparator
    .comparingInt((Bin b) -> b.currentWeight)
    .thenComparingInt(b -> b.currentVolume));

// 查找可放置的bin
Bin candidate = bins.floor(searchKey);

6.2 并行处理
利用Java Stream API并行化:

Arrays.stream(items)
    .parallel()
    .sorted(comparator)
    .forEach(item -> {
        // 分配逻辑
    });

6.3 缓存优化
预处理常用计算:

int[] prefixSum = new int[nums.length + 1];
for (int i=0; i<nums.length; i++) {
    prefixSum[i+1] = prefixSum[i] + nums[i];
}

七、正确性证明与反例分析

7.1 等和划分反例
输入:[3, 3, 3, 3]
贪心输出:[[3,3], [3,3]](正确)
输入:[4, 4, 4, 6]
贪心失败:需要动态规划

7.2 最小最大和证明

  • 最大元素必属于某个子集
  • 贪心结果 G ≤ 2 * OPT
  • 实例:[9,8,7,6,5,4,3,2,1], k=3
    贪心解:19,最优解:17

八、测试用例设计

8.1 常规测试

// 等和划分测试
@Test
void testBalancedPartition() {
    assertTrue(canPartition(new int[]{1,5,11,5}));
    assertFalse(canPartition(new int[]{1,2,3,5}));
}

// 最小最大和测试
@Test
void testMinMaxSum() {
    assertEquals(18, minMaxSum(new int[]{7,2,5,10,8}, 2));
}

8.2 边界测试

// 单个元素测试
@Test
void testSingleElement() {
    assertFalse(canPartition(new int[]{5}));
}

// 空输入测试
@Test
void testEmptyInput() {
    assertEquals(0, minMaxSum(new int[]{}, 0));
}

8.3 性能测试

// 生成10^5个元素的大规模测试
int[] bigData = new int[100000];
Arrays.fill(bigData, 1);
long start = System.currentTimeMillis();
assertTrue(canPartition(bigData));
System.out.println("Time cost: " + (System.currentTimeMillis()-start) + "ms");

九、实际应用案例

9.1 云计算资源分配

  • 需求:将虚拟机实例分配到物理机,最小化使用主机数量
  • 策略
    1. 按虚拟机资源需求(CPU+内存)降序排序
    2. 使用首次适应递减算法分配

9.2 物流装箱优化

  • 需求:装车时同时考虑货物重量和体积
  • 实现
    public class CargoOptimizer {
        // 类似多维划分实现
    }
    

9.3 分布式计算

  • 场景:将大数据作业分片到计算节点
  • 优化:根据节点处理能力动态调整划分策略

十、总结

10.1 算法选择指南

问题类型推荐算法时间复杂度适用场景
小规模精确划分动态规划O(n*sum)元素较少
大规模近似划分贪心算法O(n log n)实时性要求高
多约束复杂划分元启发式算法-复杂工业场景

更多资源:

https://www.kdocs.cn/l/cvk0eoGYucWA

本文发表于【纪元A梦】!

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

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

相关文章

数论~~~

质数 质数Miller-Rabin算法质因子分解质数筛埃氏筛欧拉筛如果只是计数&#xff0c;埃氏筛改进 快速幂乘法快速幂矩阵快速幂1维k阶实战(提醒&#xff1a;最好在mul函数中作乘法时加上&#xff08;long long&#xff09;的强制类型转换 &#xff0c;或者全部数组换成long long&am…

web第十次课后作业--Mybatis的增删改查

&#xff08;一&#xff09;删除操作 功能&#xff1a;根据主键删除数据 SQL 语句 -- 删除id17的数据 delete from emp where id 17;Mybatis 框架让程序员更关注于 SQL 语句 接口方法 Mapper public interface EmpMapper {//Delete("delete from emp where id 17&qu…

贪心算法应用:集合覆盖问题详解

贪心算法与集合覆盖问题详解 贪心算法在组合优化问题中展现出独特优势&#xff0c;集合覆盖问题&#xff08;Set Cover Problem&#xff09;是其中的经典案例。本文将用2万字全面解析贪心算法在集合覆盖/划分中的应用&#xff0c;涵盖算法原理、正确性分析、Java实现、复杂度证…

【知识点】第7章:文件和数据格式化

文章目录 知识点整理文件概述文件的打开和关闭文件的读操作文件的写操作 练习题填空题选择题​​ 知识点整理 文件概述 文件是一个存储在辅助存储器上的数据序列&#xff0c;可以包含任何数据内容。概念上&#xff0c;文件是数据的集合和抽象&#xff0c;类似地&#xff0c;函…

NetSuite Bundle - Dashboard Refresh

儿童节快乐&#xff01; 今朝发一个Bundle&#xff0c;解决一个NetSuite Dashboard的老问题。出于性能上的考虑&#xff0c;NetSuite的Dashboard中的Portlet&#xff0c;只能逐一手工刷新。有人基于浏览器做了插件&#xff0c;可以进行自动刷新。但是在我们做项目部署时&#…

智慧赋能:移动充电桩的能源供给革命与便捷服务升级

在城市化进程加速与新能源汽车普及的双重推动下&#xff0c;移动充电桩正成为能源供给领域的一场革命。传统固定充电设施受限于布局与效率&#xff0c;难以满足用户即时、灵活的充电需求&#xff0c;而移动充电桩通过技术创新与服务升级&#xff0c;打破了时空壁垒&#xff0c;…

斐波那契数列------矩阵幂法

斐波那契数列 斐波拉楔数是我们在学递归的使用看到的题目&#xff0c;但递归法是比较慢的&#xff0c;后面我们用循环递进来写的&#xff0c;但今天我有遇到了新的方法—— 矩阵幂法&#xff08;线性代数的知识点&#xff09;。 矩阵幂法&#xff1a; F11*F10*F2; F20*F11*…

【Web应用】若依框架:基础篇21二次开发-页面调整

文章目录 ⭐前言⭐一、课程讲解⭐二、怎样选择设计模式&#xff1f;&#x1f31f;1、寻找合适的对象✨1) ⭐三、怎样使用设计模式&#xff1f;&#x1f31f;1、寻找合适的对象✨1) ⭐总结 标题详情作者JosieBook头衔CSDN博客专家资格、阿里云社区专家博主、软件设计工程师博客内…

【 java 基础知识 第一篇 】

目录 1.概念 1.1.java的特定有哪些&#xff1f; 1.2.java有哪些优势哪些劣势&#xff1f; 1.3.java为什么可以跨平台&#xff1f; 1.4JVM,JDK,JRE它们有什么区别&#xff1f; 1.5.编译型语言与解释型语言的区别&#xff1f; 2.数据类型 2.1.long与int类型可以互转吗&…

CVE-2020-17518源码分析与漏洞复现(Flink 路径遍历)

漏洞概述 漏洞名称&#xff1a;Apache Flink REST API 任意文件上传漏洞 漏洞编号&#xff1a;CVE-2020-17518 CVSS 评分&#xff1a;7.5 影响版本&#xff1a;Apache Flink 1.5.1 - 1.11.2 修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0 漏洞类型&#xff1a;路径遍历导致的任…

Excel表格批量下载 CyberWin Excel Doenlaoder 智能编程-——玄武芯辰

使用 CyberWin Excel Downloader 进行 Excel 表格及各种文档的批量下载&#xff0c;优势显著。它能大幅节省时间&#xff0c;一次性获取大量所需文档&#xff0c;无需逐个手动下载&#xff0c;提升工作效率。可确保数据完整性与准确性&#xff0c;避免因重复操作产生失误。还便…

可编辑PPT | 基于大数据中台新能源智能汽车应用解决方案汽车大数据分析与应用解决方案

这份文档是一份关于新能源智能汽车应用解决方案的详细资料&#xff0c;它深入探讨了智能汽车行业的发展趋势&#xff0c;指出汽车正从单纯交通工具转变为网络入口和智能设备&#xff0c;强调了车联网、自动驾驶、智能娱乐等技术的重要性。文档提出了一个基于大数据中台的车企数…

k8s集群安装坑点汇总

前言 由于使用最新的Rocky9.5,导致kubekey一键安装用不了&#xff0c;退回Rocky8麻烦机器都建好了&#xff0c;决定手动安装k8s&#xff0c;结果手动安装过程中遇到各种坑&#xff0c;这里记录下&#xff1b; k8s安装 k8s具体安装过程可自行搜索&#xff0c;或者deepseek; 也…

从 Stdio 到 HTTP SSE,在 APIPark 托管 MCP Server

MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09; 是一种由 Anthropic 公司于 2024 年 11 月推出的开源通信协议&#xff0c;旨在标准化大型语言模型&#xff08;LLM&#xff09;与外部数据源和工具之间的交互。 它通过定义统一的接口和通信规则…

Mysql锁及其分类

目录 InnoDb锁Shared locks(读锁) 和 Exclusive locks(写锁)Exclusive locksShared locks Intention Locks(意向锁)为什么要有意向锁&#xff1f; Record Locks&#xff08;行锁&#xff09;Gap Locks&#xff08;间隙锁&#xff09;Next-Key LocksInsert Intention Locks(插入…

Postgresql源码(146)二进制文件格式分析

相关 Linux函数调用栈的实现原理&#xff08;X86&#xff09; 速查 # 查看elf头 readelf -h bin/postgres# 查看Section readelf -S bin/postgres (gdb) info file (gdb) maint info sections# 查看代码段汇编 disassemble 0x48e980 , 0x48e9b0 disassemble main# 查看代码段某…

【设计模式-4.11】行为型——解释器模式

说明&#xff1a;本文介绍行为型设计模式之一的解释器模式 定义 解释器模式&#xff08;Interpreter Pattern&#xff09;指给定一门语言&#xff0c;定义它的文法的一种表示&#xff0c;并定义一个解释器&#xff0c;该解释器使用该表示来解释语言中的句子。解释器模式是一种…

【已解决】MACOS M4 芯片使用 Docker Desktop 工具安装 MICROSOFT SQL SERVER

1. 环境准备 确认 Docker Desktop 配置 确保已安装 Docker Desktop for Mac (Apple Silicon)&#xff08;版本 ≥ 4.15.0&#xff09;。开启 Rosetta&#xff08;默认开启&#xff09;&#xff1a; 打开 Docker Desktop → Settings → General → Virtual Machine Options …

Quipus系统的视频知识库的构建原理及使用

1 原理 VideoRag在LightRag基础上增加了对视频的处理&#xff0c;详细的分析参考LightRag的兄弟项目VideoRag系统分析-CSDN博客。 Quipus的底层的知识库的构建的核心流程与LightRag类似&#xff0c;但在技术栈的选择和处理有所不同。Quipus对于视频的处理实现&#xff0c;与Vi…

web3-去中心化金融深度剖析:DEX、AMM及兑换交易传播如何改变世界

web3-去中心化金融深度剖析&#xff1a;DEX、AMM及兑换交易传播如何改变世界 金融问题 1.个人投资&#xff1a;在不同的时间和可能的情况&#xff08;状态&#xff09;下积累财富 2.商业投资&#xff1a;为企业家和企业提供投资生产性活动的资源 目标&#xff1a;跨越时间和…