华为OD题目: 整理扑克牌

package com.darling.boot.order.od.od12;

import java.util.*;

/**
 * 整理扑克牌
 * 题目描述: 给定一组数字,表示扑克牌的牌面数字,忽略扑克牌的花色,请按如下规则对这一组扑克牌进行整理:
 * 步骤1、
 * 对扑克牌进行分组,形成组合牌,规则如下:
 * 当牌面数字相同张数大于等于4时,组合牌为“炸弹”;
 * 3张相同牌面数字 + 2张相同牌面数字,且3张牌与2张牌不相同时,组合牌为“葫芦”;
 * 3张相同牌面数字,组合牌为“三张”;
 * 2张相同牌面数字,组合牌为“对子”;
 * 剩余没有相同的牌,则为“单张”;
 * <p>
 * 步骤2、
 * 对上述组合牌进行由大到小排列,规则如下:
 * 不同类型组合牌之间由大到小排列规则:“炸弹” > “葫芦” > “三张” > “对子” > “单张”;
 * 相同类型组合牌之间,除“葫芦”外,按组合牌全部牌面数字加总由大到小排列;
 * “葫芦”则先按3张相同牌面数字加总由大到小排列,3张相同牌面数字加总相同时,再按另外2张牌面数字加总由大到小排列;
 * 由于“葫芦”>“三张”,因此如果能形成更大的组合牌,也可以将“三张”拆分为2张和1张,其中的2张可以和其它“三张”重新组合成“葫芦”,剩下的1
 * 张为“单张”
 * <p>
 * 步骤3、
 * 当存在多个可能组合方案时,按如下规则排序取最大的一个组合方案:
 * 依次对组合方案中的组合牌进行大小比较,规则同上;
 * 当组合方案A中的第n个组合牌大于组合方案B中的第n个组合牌时,组合方案A大于组合方案B;
 * 输入描述: 第一行为空格分隔的N个正整数,每个整数取值范围[1,13],N的取值范围[1,1000]
 * 输出描述: 经重新排列后的扑克牌数字列表,每个数字以空格分隔
 * 示例
 * 示例1
 * 输入: 1 3 3 3 2 1 5
 * 输出: 3 3 3 1 1 5 2
 * <p>
 * 示例2
 * 输入: 4 4 2 1 2 1 3 3 3 4
 * 输出: 4 4 4 3 3 2 2 1 1 3
 * <p>
 * 输入: 4 4 2 1 2 1 3 3 3 4 5 5 5
 * 输出: 5 5 5 4 4 3 3 3 2 2 1 1 4
 *
 * 解题思路:
 * 本题比较复杂,
 * 主要步骤分为以下几步:
 * 1:首先用一个map来存放每个面值对应的个数,key是面值,value是个数
 * 2:如果有炸弹,处理炸弹的类型,因为题目要求,(相同类型组合牌之间,除“葫芦”外,按组合牌全部牌面数字加总由大到小排列;),
 * 所以,得用一个boomMap专门存放,key是面值,value为面值的个数之和,比如面值是5,个数是6,那么value就是5*6=30.
 * 按照value进行降序排序,加入到结果集里面
 * 3:处理葫芦的情况,如果三张类型的的个数,有两个或者两个以上,先将三张的最大的面值加入结果集,并在num3里移除,方便下面的操作
 * 那么分情况讨论,如果没有两张或者,两张的最大的面值小于移除后的三张最大的面值,那么得将三张进行拆开,拆成两张凑成葫芦,剩下一张放入一张nums1里面
 * 这里要用while循环,依次操作,知道不能为止
 * 如果经过上面的循环后,三张的类型还是>0,那么就不用拆开了,此时其实三张只有一个,按照题目要求进行处理就行
 * 4:剩下的就是处理两张和一张了
 *
 *
 */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        String[] strings = line.split(" ");
        //map用来存放每个值对应的个数
        Map<Integer, Integer> map = new HashMap<>();
        for (String str : strings) {
            int key = Integer.parseInt(str);
            Integer val = map.getOrDefault(key, 0);
            map.put(key, val + 1);
        }
        List<Integer> res = new ArrayList<>();

        List<Integer> nums1 = new ArrayList<>();
        List<Integer> nums2 = new ArrayList<>();
        List<Integer> nums3 = new ArrayList<>();
        List<Integer> nums4 = new ArrayList<>();
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            //如果个数大于等于4,用num4 进行存放
            int value = entry.getValue();
            int key = entry.getKey();
            if (value >= 4) {
                nums4.add(key);
            } else if (value == 3) {
                nums3.add(key);
            } else if (value == 2) {
                nums2.add(key);
            } else {
                nums1.add(key);
            }
        }
        if (nums4.size() > 0) {
            //这个map专门用来存放不同数值的炸弹,key是num,value是相同面值的个数加起来之和
            Map<Integer, Integer> boomMap = new HashMap<>();
            for (int num : nums4) {
                boomMap.put(num, num * map.get(num));
            }
            //按照value进行排序,从大到小, (相同类型组合牌之间,除“葫芦”外,按组合牌全部牌面数字加总由大到小排列;)
            Set<Map.Entry<Integer, Integer>> entrySet = boomMap.entrySet();
            List<Map.Entry<Integer, Integer>> list = new ArrayList<>(entrySet);
            list.sort((a1, a2) -> a2.getValue() - a1.getValue());
            //将炸弹依次存逐个入到res 的list里面
            for (Map.Entry<Integer, Integer> entry : list) {
                Integer key = entry.getKey();
                Integer value = entry.getValue();
                for (int i = 0; i < value; i++) {
                    res.add(key);
                }
            }
        }
        //对下面几个list进行降序排列
        nums3.sort((a1, a2) -> a2 - a1);
        nums2.sort((a1, a2) -> a2 - a1);

        //如果三张 类型的个数有两个或以上
        while (nums3.size() > 1) {
            res.add(nums3.get(0));
            res.add(nums3.get(0));
            res.add(nums3.get(0));
            nums3.remove(0);
            //下面是要把两个三张拆成葫芦的情况,即没有两张,或者三张的面值比两张的大
            if (nums2.size() <= 0 || nums3.get(0) > nums2.get(0)) {
                //两个放入葫芦里,一个放入单个里面
                res.add(nums3.get(0));
                res.add(nums3.get(0));
                nums1.add(nums3.get(0));
                nums3.remove(0);
            }else if (nums2.size() > 0) {
                //否则 不需要拆三张了,只是单纯的将两张组成葫芦
                res.add(nums2.get(0));
                res.add(nums2.get(0));
                nums2.remove(0);
            }
        }
        //如果三张 类型的个数1个,不需要将三张进行拆开了
        while (nums3.size() > 0 ) {
            res.add(nums3.get(0));
            res.add(nums3.get(0));
            res.add(nums3.get(0));
            nums3.remove(0);
            if (nums2.size() > 0) {
                res.add(nums2.get(0));
                res.add(nums2.get(0));
                nums2.remove(0);
            }
        }
        //将所有的两张加入结果集里面
        if (nums2.size() > 0) {
            for (int num: nums2) {
                res.add(num);
                res.add(num);
            }
        }
        if (nums1.size() > 0) {
            nums1.sort((a1, a2) -> a2 - a1);
            res.addAll(nums1);
        }
        for (int num: res) {
            System.out.print(num + " ");
        }
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值