题意

给出两个不同的斗地主中有效的牌组合,输出更大的那一组牌,如果无法比较,输出ERROR

方法

转态转换与实现

因为扑克牌描述上,2比3大,又有J,Q,K和大小王,如果把这些都写到 比较过程中,if的判断条件太过于复杂了

所以这里做了一个映射

3 4 5 6 7 8 9 10 J Q K A 2 joker JOKER
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

把它们映射到 0~14的整数,这样它们的大小关系和原来保持一致,而可以减少大量的if


当我们把值变成数值后,接下来第二部分就是把牌列表转换成牌型,这里同样做一个映射,每次输出一个pair<类型,最小值>

炸弹 个子 对子 三个 顺子
{-1,最小值} {0,最小值} {1,最小值} {2,最小值} {3/4/5/6...,最小值}

这样映射以后,只用关注是否有-1,因为炸弹能和任何类型比较,剩余的就是类型是否相同,如果相同,比较最小值即可


上面两个映射都做成函数,剩下就是遍历和简单比较了

代码

#include<bits/stdc++.h>
using namespace std;

// 3 4 5 6 7 8 9 10 J Q  K  A  2 joker JOKER
// 0 1 2 3 4 5 6 7  8 9 10 11 12 13  14
int s2int(const char * s,int n){ // 字符串转值
    if(n == 2)return 7; // 10
    if(n == 5){
        if(s[0] == 'j')return 13; // joker
        else return 14; // JOKER
    }
    if(s[0] == '2')return 12;
    if(s[0] == 'A')return 11;
    if(s[0] == 'K')return 10;
    if(s[0] == 'Q')return 9;
    if(s[0] == 'J')return 8;
    return s[0]-'3';
}

// 数组 转 类型 和 值
// 炸弹 个子 对子 三个 顺子 
// -1   0   1    2   3,4,5,6
pair<int,int> a2tv(const vector<int>&arr){
    if(arr.size() == 1)return {0,arr[0]}; // 个
    if(arr.size() == 2){
        if(arr[0] == arr[1])return {1,arr[0]}; // 对
        else return {-1,arr[0]}; // 王炸
    }
    if(arr.size() == 3){
        if(arr[0] == arr[1])return {2,arr[0]}; // 三
        else return {3,arr[0]}; // 顺
    }
    if(arr.size() == 4){
        if(arr[0] == arr[1]) return {-1,arr[0]}; // 炸
        else return {4,arr[0]}; // 顺
    }
    return {arr.size(), arr[0]}; // 顺
}

int main() {
    string s;
    while(getline(cin,s)){
        int n = s.length();
        vector<int>arr[2] = {};
        int itr = 0;
        int dpos = 0; // - 的下标
        for(int i = 0;i <= n;i++){
            if(s[i] == ' ' || s[i] == '-' || s[i] == '\0'){ // 分割字符
                arr[!!dpos].push_back(s2int(s.c_str()+itr,i-itr));
                if(s[i] == '-') dpos = i;
                itr = i+1;
            }
        }
        s[dpos] = '\0';

        auto ltv = a2tv(arr[0]);
        auto rtv = a2tv(arr[1]);
        if(ltv.first == rtv.first){ // 同类型
            if(ltv.second < rtv.second){
                printf("%s\n",s.c_str()+dpos+1);
            }else{
                printf("%s\n",s.c_str());
            }
        }else if(ltv.first == -1 || rtv.first == -1){ // 至少一个是炸弹
            if(rtv.first == -1){
                printf("%s\n",s.c_str()+dpos+1);
            }else{
                printf("%s\n",s.c_str());
            }
        }else{ // 非同类型无法比较
            printf("ERROR\n");
        }
    }
    return 0;
}

复杂度分析

时间复杂度: 所有的操作都是常数级别, 所以总复杂度为O(n)O(n)

空间复杂度: 存储字符串以外都是常数个数的额外变量,所以空间复杂度为O(n)O(n)

减少if,增加配置表,判断顺序优化

把牌转变成数字的过程用了很多if,如果转换成表会更加易读。

输出的部分,既然是if/else 加单条语句,就可以用三目运算简化

同时牌型计算如果把顺的逻辑判断提前,也可以减少if

代码

#include<bits/stdc++.h>
using namespace std;

// 3 4 5 6 7 8 9 10 J Q  K  A  2 joker JOKER
// 0 1 2 3 4 5 6 7  8 9 10 11 12 13  14
int s2int(const char * s,int n){ // 字符串转值
    pair<char*,int> si[] = { // 字符串 和 值
        {"3",0},
        {"4",1},
        {"5",2},
        {"6",3},
        {"7",4},
        {"8",5},
        {"9",6},
        {"10",7},
        {"J",8},
        {"Q",9},
        {"K",10},
        {"A",11},
        {"2",12},
        {"joker",13},
        {"JOKER",14},
    };
    for(int i =0; i<15; i++){ // 因为前两个字符各不相等,只用比前两个
        if(s[0] == si[i].first[0] && (n == 1?'\0':s[1]) == si[i].first[1] ){ // 超过长度的部分是 '\0'
            return si[i].second;
        }
    }
    assert(false);
    return -1;
}

// 数组 转 类型 和 值
// 炸弹 个子 对子 三个 顺子 
// -1   0   1    2   3,4,5,6
pair<int,int> a2tv(const vector<int>&arr){
    if(arr.size() == 1)return {0,arr[0]}; // 个
    if(arr.size() == 2){
        if(arr[0] == arr[1])return {1,arr[0]}; // 对
        else return {-1,arr[0]}; // 王炸
    }
    if(arr[0] < arr[1]) {
        return {arr.size(), arr[0]}; // 顺
    }
    if(arr.size() == 3){
        return {2,arr[0]}; // 三
    }
    if(arr.size() == 4){
        return {-1,arr[0]}; // 炸
    }
    assert(false);
    return {-1, -1};
}

int main() {
    string s;
    while(getline(cin,s)){
        int n = s.length();
        vector<int>arr[2] = {};
        int itr = 0;
        int dpos = 0; // - 的下标
        for(int i = 0;i <= n;i++){
            if(s[i] == ' ' || s[i] == '-' || s[i] == '\0'){ // 分割字符
                arr[!!dpos].push_back(s2int(s.c_str()+itr,i-itr));
                if(s[i] == '-') dpos = i;
                itr = i+1;
            }
        }
        s[dpos] = '\0';

        auto ltv = a2tv(arr[0]);
        auto rtv = a2tv(arr[1]);
        if(ltv.first == rtv.first){ // 同类型
            printf("%s\n",s.c_str()+(ltv.second < rtv.second?dpos+1:0));
        }else if(ltv.first == -1 || rtv.first == -1){ // 至少一个是炸弹
            printf("%s\n",s.c_str()+(rtv.first == -1?dpos+1:0));
        }else{ // 非同类型无法比较
            printf("ERROR\n");
        }
    }
    return 0;
}

复杂度分析

时间复杂度: 所有的操作都是常数级别, 所以总复杂度为O(n)O(n)

空间复杂度: 存储字符串以外都是常数个数的额外变量,所以空间复杂度为O(n)O(n)