算法题 分糖果

575. 分糖果

问题描述

Alice 有 n 颗糖果,其中第 i 颗糖果的类型为 candyType[i]。Alice 注意到她的体重正在增加,所以她想在吃糖果的同时保持身材。

她只允许自己吃 n / 2 颗糖果,并且希望在吃掉的糖果中,不同种类的糖果数量尽可能多

返回 Alice 可以吃到的不同种类糖果数量的最大值。

示例

输入: candyType = [1,1,2,2,3,3]
输出: 3
解释: 有6颗糖果,Alice可以吃3颗。有3种不同糖果,她可以每种吃一颗。

输入: candyType = [1,1,2,3]
输出: 2
解释: 有4颗糖果,Alice可以吃2颗。有3种不同糖果,但她最多只能吃2颗,所以最多2种。

算法思路

核心

这是一个典型的限制条件下的最大化问题,有两个限制因素:

  1. 数量限制:最多只能吃 n/2 颗糖果
  2. 种类限制:最多只能吃到所有不同种类的数量

贪心策略

要使吃到的糖果种类最多,应该:

  1. 优先选择不同种类的糖果
  2. 每种糖果只吃一颗(因为目标是最大化种类数,不是数量)

数学分析

设:

  • total = 糖果总数 = n
  • maxEat = 最多能吃的糖果数 = n / 2
  • types = 不同糖果种类的数量

则最大种类数 = min(maxEat, types)

为什么?

  • 如果种类数 ≤ 能吃的数量:可以吃到所有种类
  • 如果种类数 > 能吃的数量:最多只能吃到 maxEat

代码实现

import java.util.HashSet;
import java.util.Set;

class Solution {
    /**
     * 计算Alice能吃到的不同糖果种类的最大数量
     * 
     * @param candyType 糖果类型数组,candyType[i]表示第i颗糖果的类型
     * @return 能吃到的不同糖果种类的最大数量
     */
    public int distributeCandies(int[] candyType) {
        int n = candyType.length;           // 糖果总数
        int maxEat = n / 2;                 // 最多能吃的糖果数量
        
        // 使用HashSet统计不同糖果种类的数量
        Set<Integer> types = new HashSet<>();
        for (int type : candyType) {
            types.add(type);
        }
        
        int totalTypes = types.size();      // 不同糖果种类的总数
        
        // 返回两个限制中的较小值:
        // 1. 最多能吃的糖果数量
        // 2. 不同糖果种类的总数
        return Math.min(maxEat, totalTypes);
    }
}

优化(使用Java 8 Stream)

import java.util.Arrays;

class Solution {
    /**
     * 优化:使用Java 8 Stream API
     * 
     * @param candyType 糖果类型数组
     * @return 能吃到的不同糖果种类的最大数量
     */
    public int distributeCandies(int[] candyType) {
        int maxEat = candyType.length / 2;
        
        // 使用Stream统计不同种类的数量
        long uniqueTypes = Arrays.stream(candyType)
                                .distinct()
                                .count();
        
        return (int) Math.min(maxEat, uniqueTypes);
    }
}

算法分析

  • 时间复杂度:O(n)

    • 遍历数组一次统计种类数
    • HashSet的add操作平均O(1)
  • 空间复杂度:O(n)

    • HashSet最多存储n个不同元素
    • 最坏情况下所有糖果都不同
  • 关键

    • 贪心策略避免了复杂的动态规划
    • 数学分析得出简单公式 min(n/2, 不同种类数)

算法过程

candyType = [1,1,2,2,3,3]

1:计算基本参数

  • 糖果总数 n = 6
  • 最多能吃 maxEat = 6 / 2 = 3

2:统计不同种类

  • 种类集合:{1, 2, 3}
  • 不同种类数 totalTypes = 3

3:计算结果

  • min(3, 3) = 3

策略执行

  • 吃1颗类型1的糖果
  • 吃1颗类型2的糖果
  • 吃1颗类型3的糖果
  • 共吃3颗,3种不同糖果

测试用例

public static void main(String[] args) {
    Solution solution = new Solution();
    
    // 测试用例1:标准示例
    int[] candyType1 = {1,1,2,2,3,3};
    System.out.println("Test 1: " + solution.distributeCandies(candyType1)); // 3
    
    // 测试用例2:种类多于能吃的数量
    int[] candyType2 = {1,1,2,3};
    System.out.println("Test 2: " + solution.distributeCandies(candyType2)); // 2
    // n=4, maxEat=2, types=3 → min(2,3)=2
    
    // 测试用例3:种类少于能吃的数量
    int[] candyType3 = {1,1,1,1};
    System.out.println("Test 3: " + solution.distributeCandies(candyType3)); // 1
    // n=4, maxEat=2, types=1 → min(2,1)=1
    
    // 测试用例4:所有糖果都不同
    int[] candyType4 = {1,2,3,4,5,6,7,8};
    System.out.println("Test 4: " + solution.distributeCandies(candyType4)); // 4
    // n=8, maxEat=4, types=8 → min(4,8)=4
    
    // 测试用例5:单一种类
    int[] candyType5 = {1};
    System.out.println("Test 5: " + solution.distributeCandies(candyType5)); // 1
   
    // 实际上,根据题目约束,n总是偶数
    // 测试用例6:两个元素
    int[] candyType6 = {1,2};
    System.out.println("Test 6: " + solution.distributeCandies(candyType6)); // 1
    // n=2, maxEat=1, types=2 → min(1,2)=1
}

关键点

  1. 贪心选择的正确性

    • 目标是最大化种类数,不是数量
    • 所以每种糖果只吃一颗是最优策略
    • 吃多颗同种糖果不会增加种类数
  2. 两个限制因素

    • 数量限制:最多吃 n/2
    • 种类限制:最多有 uniqueTypes
    • 最终结果是两者的最小值
  3. 数据结构选择

    • HashSet适合统计唯一元素
    • 也可以用数组(如果类型范围小)或排序去重
  4. 边界情况

    • 所有糖果同一种类
    • 所有糖果都不同种类
    • 最小输入(2颗糖果)

常见问题

  1. 为什么用HashSet而不是数组?

    • 糖果类型值范围可能很大(题目约束:-10⁵ ≤ candyType[i] ≤ 10⁵)
    • 使用数组需要200001个空间,可能浪费
    • HashSet动态分配,更节省空间
  2. 如果n是奇数怎么办?

    • 题目约束:n是偶数,且2 ≤ n ≤ 10⁴
    • 所以 n/2 总是整数
  3. 能否用排序解决?

    • 可以,先排序再遍历统计不同种类
    • 时间复杂度O(n log n),比HashSet方法慢
  4. 算法的直观理解

    • 想象糖果按种类分堆
    • Alice要从这些堆中选糖果吃
    • 她的策略是:每堆只拿一颗,这样能吃到最多种类
    • 如果堆数 ≤ 能吃的数量,她可以每堆都拿一颗
    • 如果堆数 > 能吃的数量,她最多只能吃到能吃数量的种类
  5. 为什么这是贪心算法

    • 每一步都做局部最优选择:选择未吃过的新种类
    • 这种策略最终得到全局最优
    • 因为问题具有贪心选择性质:优先选择不同种类不会影响后续选择
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值