leetcode
leetcode 2901 ~ 2950
最多牌组数

最多牌组数

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 387 ms, 内存: 19.7 MB


import java.util.*;
import java.util.stream.Collectors;

/**
 * This method finds the maximum number of valid Mahjong groups (either sequences or triplets)
 * that can be formed from the given tiles using Java Streams.
 * 
 * @param tiles an array of integers representing the Mahjong tiles
 * @return the maximum number of valid groups
 */
public class MahjongGroupsStream {
    public static int maxGroups(int[] tiles) {
        // Count the frequency of each tile
        Map<Integer, Long> tileCount = Arrays.stream(tiles)
                                             .boxed()
                                             .collect(Collectors.groupingBy(e -> e, Collectors.counting()));
        List<Integer> sortedTiles = Arrays.stream(tiles)
                                           .boxed()
                                           .sorted()
                                           .collect(Collectors.toList());
        int groups = 0;
        for (int tile : sortedTiles) {
            if (tileCount.get(tile) > 0) {
                // Try to form a triplet (3 of the same)
                if (tileCount.get(tile) >= 3) {
                    tileCount.put(tile, tileCount.get(tile) - 3);
                    groups++;
                } else if (tileCount.get(tile) > 0 && tileCount.getOrDefault(tile + 1, 0L) > 0 && tileCount.getOrDefault(tile + 2, 0L) > 0) {
                    // Try to form a sequence (3 consecutive numbers)
                    tileCount.put(tile, tileCount.get(tile) - 1);
                    tileCount.put(tile + 1, tileCount.get(tile + 1) - 1);
                    tileCount.put(tile + 2, tileCount.get(tile + 2) - 1);
                    groups++;
                }
            }
        }
        return groups;
    }
    public static void main(String[] args) {
        int[] tiles1 = {2, 2, 2, 3, 4};
        int[] tiles2 = {2, 2, 2, 3, 4, 1, 3};
        System.out.println(maxGroups(tiles1)); // Output: 1
        System.out.println(maxGroups(tiles2)); // Output: 2
    }
}

解释

方法:

本题解采用动态规划的方法来决定最大牌组数。首先,使用Counter统计每张牌的数量。定义状态dp[i, j]表示当考虑到当前数字num时,并且(num+1)剩下i张,(num+2)剩下j张时,可以形成的最大组数。遍历每个数字num,对于每个num,我们查看可以使用当前num及其后两个数字(num+1和num+2)来形成多少组顺子。考虑到num, num+1, num+2可以组成的顺子数量,并更新状态。最后,dp字典中的最大值即为答案。

时间复杂度:

O(n log n)

空间复杂度:

O(n)

代码细节讲解

🦆
如何确保动态规判断的`dp[i, j]`是有效的状态,能够覆盖所有组合情况?
在这个动态规划设计中,`dp[i, j]`表示在处理到当前数字`num`时,若`(num+1)`剩余`i`张,`(num+2)`剩余`j`张时,可以得到的最大组数。这个设计利用了牌的有序属性和连续性。动态规划的状态转移保证了每一步都是基于前一状态的最优解来进行更新的。通过枚举在当前数字`num`下,利用不同数量的`num`,`num+1`和`num+2`来形成顺子的可能性(即循环变量`k`的使用),可以确保这三个数字能组成的所有有效组合均被考虑。此外,状态转移考虑了所有可能的`i`和`j`的值,确保了状态空间的完整覆盖。
🦆
在动态规划的转移方程中,为什么选择`(x - i - k) // 3 + k`作为新状态的计算方式?
这一计算方法的选择基于如何最大化使用当前数字`num`形成的组数。这里`x`是`num`的数量,`i`是之前状态中`num+1`的剩余数量,`k`是在当前状态决定使用的组成顺子的数量。`x - i - k`表示在形成`k`个顺子后,`num`还剩下的数量。将这个剩余数量除以3是因为三张相同的牌可以单独形成一个组。因此,`(x - i - k) // 3`表示除了形成顺子外,剩余的`num`还可以独立形成的组数。加上`k`(已形成顺子的组数)得到的总和就是当前状态可以得到的最大组数。
🦆
对于处理边界条件,当`num+1`或`num+2`不存在于`tiles`中时,如何处理这些情况?
在这种情况下,可以认为`num+1`或`num+2`的牌数为0。这是因为在实际的计数中,任何未明确出现的数字其数量默认为0。因此,在动态规划过程中,当我们查看`num+1`或`num+2`的牌数时,若它们不存在于`Counter`对象中,它们的值将被视为0。这种处理自然地适应了边界条件,无需特殊处理即可在状态转移中正确使用。

相关问题