华为OD机考,整理扑克牌【目前最优解】(第一次机考,心里压力真大)

158 阅读5分钟

华为OD机考,整理扑克牌⭐题(还有⭐⭐题) 牛客这个破OJ,输入是以文件格式输入,导致我在本地IDE中写的提交到OJ总是报错,上来就搞心态。

题目描述 给定一组数字,表示扑克牌的牌面数字,忽略扑克牌的花色,请按如下规则对这一组扑克牌进行整理: 步骤1. 对扑克牌进行分组,形成组合牌,规则如下: 斜体样式当牌面数字相同张数大于等于4时,组合牌为“炸弹”; 3张相同牌面数字 + 2张相同牌面数字,且3张牌与2张牌不相同时,组合牌为“葫芦”; 3张相同牌面数字,组合牌为“三张”; 2张相同牌面数字,组合牌为“对子”; 剩余没有相同的牌,则为“单张”; 步骤2. 对上述组合牌进行由大到小排列,规则如下: 不同类型组合牌之间由大到小排列规则:“炸弹” > “葫芦” > “三张” > “对子” > “单张”; 相同类型组合牌之间,除“葫芦”外,按组合牌全部牌面数字加总由大到小排列; “葫芦”则先按3张相同牌面数字加总由大到小排列,3张相同牌面数字加总相同时,再按另外2张牌面数字加总由大到小排列; 由于“葫芦”>“三张”,因此如果能形成更大的组合牌,也可以将“三张”拆分为2张和1张,其中的2张可以和其它“三张”重新组合成“葫芦”,剩下的1张为“单张” 步骤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 测试用例2 输入:4 4 2 1 2 1 3 3 3 4 输出:4 4 4 3 3 2 2 1 1 3

我的思路:我当时考试的时候的思路就是针对map的key,val分别两次排序,其中key(牌大小)的优先级小于val(牌张数),但是map这个数据结构我是真玩不明白。然后自定义结构体数组解决问题,答题的思路相仿基数排序。 另外我没在找到OJ可以测试我代码,仅跑了两个测试用例(还有一些文章下面失败的例子,统统pass).欢迎大家评论区讨论and补充.【Tip:我相信自己思路没问题】

#include <iostream>
#include <unordered_map>
using namespace std;

typedef struct Card{
    int key;
    int val;
}card;

//针对自定义结构体魔改的冒泡排序,(考虑效率可以自行进行高效排序算法修改)
void sort(card arr[],int len){
    
    for(int i = 0;i < len-1;i++){
        for(int j = 0;j< len-i-1;j++){
            if(arr[j].val < arr[j+1].val){
                card tmp;
                tmp.val = arr[j].val;tmp.key = arr[j].key;
                arr[j].val = arr[j+1].val;arr[j].key = arr[j+1].key;
                arr[j+1].val = tmp.val;arr[j+1].key=tmp.key;
            }else if(arr[j].val == arr[j+1].val){
                if(arr[j].key < arr[j+1].key){
                    card tmp;
                    tmp.val = arr[j].val;tmp.key = arr[j].key;
                    arr[j].val = arr[j+1].val;arr[j].key = arr[j+1].key;
                    arr[j+1].val = tmp.val;arr[j+1].key=tmp.key;
                }
                
            }
        }
    }
}

/*观测题目,可以知道本题目的难点在于葫芦牌的处理,
find函数就是针对处理葫芦牌的处理写的,
寻找三带二的,二中最大的两张牌
并返回其牌目*/
int find(card arr[],int len,int idx){
    int comVal = -1;int max_i = -1;
    int i = idx+1;
    for(;i<len;i++ ){
        if(arr[i].val>=2 && comVal<arr[i].key){
            max_i = i;
            comVal = arr[i].key;
        }
    }
    arr[max_i].val -= 2;
    sort(arr,len);
    return comVal;
}

//此函数是用于打印最终结果的,第一参数是输出次数,第二参数是输出的牌目
void print(int i,int c){
    for(;i>0;i--){
        cout << c <<" ";
    }
}

void map2arr(card arr[],unordered_map<int,int> map){
    int index = 0;
    //这里就比较简单了,把map的内容copy到自己定义的数组中去
    for (auto it = map.begin(); it != map.end(); ++it) {
        arr[index].key = it->first;
        arr[index].val = it->second;
        index++;
    }
}
void done(card arr[],int len){
    sort(arr,len);
    //这里只需要对数组的第一个元素做相关操作,因为每次调用sort函数后,数组总是降序的
    while(arr[0].val!=0){
        if(arr[0].val==3){
            print(arr[0].val,arr[0].key);
            arr[0].val-=3;
            int key_ = find(arr,len,0);
        if(key_!=-1){
            print(2,key_);
        }
        }else{
            print(arr[0].val,arr[0].key);
            //这里不要奇怪,就是针对已经输出的元素更新他的val值【val值代表的是他还可以被输出多少次】
            arr[0].val-=arr[0].val;
            sort(arr,len);
        }
    }
}
int main(){
    /*我对map了解的比较少,唯一用得比较多的就是存储键值对,且键值唯一*/
    int a;
    /*我这里默认map的初始键值对元素都是0*/
    unordered_map<int,int> map;
    while(cin >> a){
        //利用map的key唯一的特性统计牌数
        map[a]=map[a]+1;
    }
    int mapSize = map.size();
    //数组比价简单,所以我用数组来解决这道题目,但是素组类型是自定义的card类型。【其实大体思路有点类似于基数排序的思想】
    card arr[mapSize];
    map2arr(arr,map);
    int len = sizeof(arr)/sizeof(arr[0]);
    done(arr,len);
    
}