贪心算法:看似精明的 “短视选手”,用好了也能逆袭!💥

想象你走进一家超豪华自助餐厅,面对琳琅满目的美食,每盘只能拿一份。你会怎么做?先抢最贵的龙虾?还是先拿一堆小蛋糕垫肚子?这种 "每次都选当前看起来最好" 的决策方式,就是编程界超有名的贪心算法!它简单粗暴,却又充满争议,今天咱们就来揭开它的真面目!

啥是贪心算法?自助餐式的 "精明决策"

贪心算法的核心思想就一句话:每次都选当前状态下最有利的选项,不考虑之后的情况。就像吃自助餐时,你不管后面还有没有更美味的,先把眼前的牛排、三文鱼拿了再说。在编程里,它常用于解决一些优化问题,比如如何用最少的硬币凑出指定金额,或者怎样在最短时间内完成多个任务。

不过,贪心算法有个致命弱点 ------目光短浅!它只看眼前利益,不考虑长远影响,就像你一口气吃撑了牛排,结果后面的帝王蟹只能眼巴巴看着。所以用它解题,有时候能 "躺赢",有时候却会翻车。

哪些情况能用贪心算法?这 3 类问题超合适!

1. 找零钱问题:硬币的 "最优组合"

假设你是收银员,顾客给了 100 元,商品价格是 63 元,现在要用最少的硬币找零(假设有面值为 25、10、5、1 元的硬币)。用贪心算法超简单:

ini 复制代码
public class CoinChange {
    public static int coinChange(int[] coins, int amount) {
        int count = 0;
        // 从大到小排序硬币面值
        java.util.Arrays.sort(coins);
        for (int i = coins.length - 1; i >= 0; i--) {
            while (amount >= coins[i]) {
                amount -= coins[i];
                count++;
            }
        }
        return amount == 0? count : -1;
    }
    public static void main(String[] args) {
        int[] coins = {25, 10, 5, 1};
        int amount = 63;
        int result = coinChange(coins, amount);
        System.out.println("最少需要 " + result + " 枚硬币");
    }
}

代码解析:先把硬币面值从大到小排序,然后从最大面值开始试,只要剩余金额够,就用这个面值的硬币,直到凑出目标金额。比如找 63 元,先拿 2 个 25 元(剩 13 元),再拿 1 个 10 元(剩 3 元),最后拿 3 个 1 元,总共 6 枚硬币,完美!

2. 活动选择问题:一天能参加多少活动?

你收到一堆活动邀请,但时间有冲突,每个活动都有开始和结束时间。贪心算法的思路是:优先选最早结束的活动,这样就能空出更多时间参加后续活动。

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
class Activity {
    int start;
    int end;
    Activity(int start, int end) {
        this.start = start;
        this.end = end;
    }
}
public class ActivitySelection {
    public static List<Activity> selectActivities(Activity[] activities) {
        Arrays.sort(activities, Comparator.comparingInt(a -> a.end));
        List<Activity> selected = new ArrayList<>();
        selected.add(activities[0]);
        int lastEnd = activities[0].end;
        for (int i = 1; i < activities.length; i++) {
            if (activities[i].start >= lastEnd) {
                selected.add(activities[i]);
                lastEnd = activities[i].end;
            }
        }
        return selected;
    }
    public static void main(String[] args) {
        Activity[] activities = {
            new Activity(1, 4),
            new Activity(3, 5),
            new Activity(0, 6),
            new Activity(5, 7),
            new Activity(3, 8),
            new Activity(5, 9),
            new Activity(6, 10),
            new Activity(8, 11),
            new Activity(8, 12),
            new Activity(2, 13),
            new Activity(12, 14)
        };
        List<Activity> result = selectActivities(activities);
        System.out.println("最多能参加 " + result.size() + " 个活动");
    }
}

代码解析:先按活动结束时间排序,选第一个活动,然后从第二个活动开始,只要开始时间晚于上一个活动的结束时间,就加入活动列表。这样下来,你能参加的活动数量就是最多的!

3. 背包问题(部分背包):小偷的 "最优盗窃策略"

小偷闯入仓库,背包容量有限,但物品可以拆分(比如金条能切)。贪心算法会优先拿单位价值(价格 / 重量)最高的物品。

ini 复制代码
class Item {
    int value;
    int weight;
    Item(int value, int weight) {
        this.value = value;
        this.weight = weight;
    }
}
public class FractionalKnapsack {
    public static double fractionalKnapsack(int capacity, Item[] items) {
        Arrays.sort(items, (a, b) -> Double.compare((double)b.value / b.weight, (double)a.value / a.weight));
        double totalValue = 0;
        int currentWeight = 0;
        for (Item item : items) {
            if (currentWeight + item.weight <= capacity) {
                currentWeight += item.weight;
                totalValue += item.value;
            } else {
                int remaining = capacity - currentWeight;
                totalValue += (double)item.value / item.weight * remaining;
                break;
            }
        }
        return totalValue;
    }
    public static void main(String[] args) {
        Item[] items = {
            new Item(60, 10),
            new Item(100, 20),
            new Item(120, 30)
        };
        int capacity = 50;
        double result = fractionalKnapsack(capacity, items);
        System.out.println("背包能装的最大价值是 " + result);
    }
}

代码解析:先按单位价值给物品排序,优先装单位价值高的。装到背包快满时,拆分最后一个物品填满剩余空间,这样就能算出背包的最大价值。

贪心算法的 "翻车现场":这些坑千万别踩!

虽然贪心算法在上面这些场景很能打,但遇到下面这些问题,它就会瞬间 "露馅":

1. 0 - 1 背包问题:物品不能拆分的尴尬

还是小偷背包的例子,但这次物品不能拆分(比如整台电脑)。贪心算法可能先拿了价值高但很重的物品,导致后面装不下其他东西,结果总价值反而不是最优。比如背包容量 10,有 3 个物品:[价值 60,重量 5]、[价值 100,重量 7]、[价值 120,重量 9],贪心算法会先选第二个物品,而最优解其实是选第一个和第三个。

2. 图的最短路径(复杂情况):只看眼前会迷路

在一个复杂的地图(图结构)里找最短路径,如果用贪心算法只选当前看起来最近的点,很可能走到死胡同,错过真正的最短路线。这时候就得用 Dijkstra 算法或弗洛伊德算法这类 "全局视野" 的方法。

3. 找零钱特殊货币:面值不按套路出牌

如果硬币面值是 [1, 3, 4],想凑出 6 元,贪心算法会先拿 4 元,再拿两个 1 元,共 3 枚硬币。但最优解是两个 3 元,只需要 2 枚硬币。这就是因为贪心算法没考虑到整体最优。

总结:贪心算法的 "正确打开方式"

贪心算法就像一把双刃剑:

  • 优点:思路简单,代码好写,计算速度快,在某些问题上能快速得到最优解;
  • 缺点:太 "短视",容易陷入局部最优,面对复杂问题容易翻车。

想用贪心算法,记住这个口诀:先判断问题是否满足贪心选择性质和最优子结构。简单来说,就是每次局部最优选择不会影响最终全局最优。如果满足,大胆用它 "开挂";如果不确定,还是老老实实换其他算法吧!

相关推荐
芜湖xin几秒前
【题解-洛谷】B4278 [蓝桥杯青少年组国赛 2023] 简单算术题
算法·
理智的灰太狼2 分钟前
题目 3298: 蓝桥杯2024年第十五届决赛真题-兔子集结
算法·职场和发展·蓝桥杯
kingmax542120083 小时前
【洛谷P9303题解】AC- [CCC 2023 J5] CCC Word Hunt
数据结构·c++·算法·广度优先
fanged4 小时前
构建系统maven
java·maven
沙滩小岛小木屋4 小时前
maven编译时跳过test过程
java·maven
白熊1884 小时前
【机器学习基础】机器学习入门核心算法:XGBoost 和 LightGBM
人工智能·算法·机器学习
bai_lan_ya4 小时前
数据结构-排序-排序的七种算法(2)
数据结构·算法·排序算法
江沉晚呤时5 小时前
SQL Server 事务详解:概念、特性、隔离级别与实践
java·数据库·oracle·c#·.netcore
还是鼠鼠5 小时前
单元测试-概述&入门
java·开发语言·后端·单元测试·log4j·maven
全域智图6 小时前
元胞自动机(Cellular Automata, CA)
人工智能·算法·机器学习