无技术含量的顺序检索

· · 题解

AT262:皇家同花顺

题目简述

输入一长串字符串,为一副牌的顺序。需要凑成一顺同花顺,输出凑成同花顺之前的牌,无牌则输出0。保证有解。

思路

由于题面中已经强调了只有一副牌,显然每张牌只能出现一次。那么,则每当 10JQKA 出现的时候将该牌色(CSDH)的计数器加一,则当某一牌色计数器为“5”时,同花顺匹配完成。

使用 char 数组存入与读取。由于有绝对不会出错的 cin >> [char数组] 所以非常方便。

10 需要特判。由于数字 1 只能属于 10 ,所以可以在读到 1 的时候再读一遍。此外,由于数据一定会存在 10 ,所以不可以使用判断能否被 2 整除来判断是否可以记录数据。因此,我使用了 Counter “计数器”记录该字母是牌色还是牌上数字。

此题强制要求最后换行不要忘记了。

其余的看代码就好啦!

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

char a[54];//通过 char 数组实现读入与随时访问。由于每张牌只能出现一次且到同花顺完成时就会结束,实际上数组牌数最多只需要录入 49 张,最多需要录入 53 个字符(读入四个“10”)。额外的部分不需要读入也就不用开。
int order, i, j, Counter = 1, C, H, D, S;// CSDH 计录出现数,由于每种牌只会出现一次,所以 CSDH 中任意值为五时,同花顺就已经匹配完成。 Counter 标记是第一次录入还是第二次录入来确定是否需要计数。 i 为花色, j 为牌面的大小。 order 则是更新当前访问的字母。
int main()
{
    cin >> a;
    while(true){
        switch(a[order++])//两句合一句的 a[order++] 注意是先进行switch的判断句,再进行 order++!
        {
        case 'S':
            i = 1, Counter = 1;
            break;
        case 'H':
            i = 2, Counter = 1;
            break;
        case 'D':
            i = 3, Counter = 1;
            break;
        case 'C':
            i = 4, Counter = 1;//counter = 1 时不会录入
            break;
        case 'A':
            j = 1, Counter = 2;//没有“1”, 只有 A 。
            break;
        case '2':
            j = 2, Counter = 2;
            break;
        case '3':
            j = 3, Counter = 2;
            break;
        case '4':
            j = 4, Counter = 2;
            break;
        case '5':
            j = 5, Counter = 2;
            break;
        case '6':
            j = 6, Counter = 2;
            break;
        case '7':
            j = 7, Counter = 2;
            break;
        case '8':
            j = 8, Counter = 2;
            break;
        case '9':
            j = 9, Counter = 2;
            break;
        case '0':
            j = 10, Counter = 2;
            break;
        case 'J':
            j = 11, Counter = 2;
            break;
        case 'Q':
            j = 12, Counter = 2;
            break;
        case 'K':
            j = 13, Counter = 2;//counter = 2时记录。
            break;
        default:
              break;//“1”的时候再次读入。
    }
        if(Counter == 2){//每两次计数
            if(j>=10||j==1)//j 为 1、10、11、12、13 时为需要的同花顺牌
                switch(i)
                {
                case 1:
                    S++;//记录够同花顺的牌数
                    break;
                case 2:
                    H++;
                    break;
                case 3:
                    D++;
                    break;
                case 4:
                    C++;
                    break;
                }
            if(C==5||H==5||D==5||S==5)//到五踢出
                break;
        }
    }

    j = order, Counter = 0;//Counter 在这里变成对输出非同花顺牌的计数器,用来判断用不用输出零,就不新定义一个变量了。
    if(S == 5) i = 1;
    if(H == 5) i = 2;//判断是哪一组牌先同花顺。
    if(D == 5) i = 3;
    if(C == 5) i = 4;

    for(order = 0; order <= j; order++)//因为有“10”的存在,所以不可用(order % 2 == 0)来判断。
    {
            switch (i) {//超级暴力的判断
            case 1:
                if(a[order] == 'S')
                {
                    order++;
                    if(a[order] == 'J'||a[order] == 'Q'||a[order] == 'K'||a[order] == 'A') {S--;if(S == 0) {goto label;}}//S 必等于五,所以 S 为零是同花顺就已经完成。用 goto label 免得因为是 switch 中,在某些评测环境中可能会被判断没有循环而 continue 出错。
                    else if(a[order] == '1') {order+=1;S--;if(S == 0) {goto label;}}//为“ 10” 时 order 多加个一。
                    else {printf("%c%c", a[order-1], a[order]);Counter++;}不满足上面的就是花色正确但是牌面不正确因此要输出。counter 别忘了自加来记录有没有输出过别的牌。
                }else{
                    Counter++;
                    printf("%c", a[order]);
                    order++;
                    printf("%c", a[order]);
                    if(a[order]=='1') {//“10”的特判不要忘了。
                        order++;
                        printf("%c", a[order]);
                    }
                }
                break;
            case 2://下面就是复制粘贴查找替换的事。
                if(a[order] == 'H')
                {
                    order++;
                    if(a[order] == 'J'||a[order] == 'Q'||a[order] == 'K'||a[order] == 'A') {H--;if(H == 0){goto label;}}
                    else if(a[order] == '1') {order++;H--;if(H == 0) {goto label;}}
                    else {printf("%c%c", a[order-1], a[order]);Counter++;}
                }else{
                    Counter++;
                    printf("%c", a[order]);
                    order++;
                    printf("%c", a[order]);
                    if(a[order]=='1') {
                        order++;
                        printf("%c", a[order]);
                    }
                }
                break;
            case 3:
                if(a[order] == 'D')
                {
                    order++;
                    if(a[order] == 'J'||a[order] == 'Q'||a[order] == 'K'||a[order] == 'A') {D--;if(D == 0){goto label;}}
                    else if(a[order] == '1') {order++;D--;if(D == 0){goto label;}}
                    else {printf("%c%c", a[order-1], a[order]);Counter++;}
                }else{
                    Counter++;
                    printf("%c", a[order]);
                    order++;
                    printf("%c", a[order]);
                    if(a[order]=='1') {
                        order++;
                        printf("%c", a[order]);
                    }
                }
                break;
            case 4:
                if(a[order] == 'C')
                {
                    order++;
                    if(a[order] == 'J'||a[order] == 'Q'||a[order] == 'K'||a[order] == 'A') {C--;if(C==0) goto label;}
                    else if(a[order] == '1') {order++;C--;if(C == 0){goto label;}}
                    else {printf("%c%c", a[order-1], a[order]);Counter++;}
                }else{
                    Counter++;
                    printf("%c", a[order]);
                    order++;
                    printf("%c", a[order]);
                    if(a[order]=='1') {
                        order++;
                        printf("%c", a[order]);
                    }
                }
            }
    }
    label:
    if(Counter == 0) printf("0\n");
    else printf("\n");//别忘换行
    return 0;
}

相信上面的代码虽然清楚但繁琐而臃肿,并不是最优的复杂度。对代码的修正有一下几点:

实现牌色与牌面数值的分别录入,取消 Counter 这一计数器。

缔消输出时对原字符串的重判断,将原字符串转为数组,优化复杂度并减少代码量。

优化对输出零的判断:如果 order == 4 说明前面只有同花顺的牌面,直接输出零即可。

其余部分见代码。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

    char cina[53];
    int order, i, j, card_color[49], card_number[49], card[5];

int main()
{
    cin >> cina;
    while(true)
    {
        switch(cina[i])
        {
            case 'S':
                card_color[order] = 1;
                break;
            case 'H':
                card_color[order] = 2;
                break;
            case 'D':
                card_color[order] = 3;
                break;
            case 'C':
                card_color[order] = 4;
                break;
        }
        i++;
        switch(cina[i])
        {
        case 'A':
            card_number[order] = 1;
            break;
        case 'J':
            card_number[order] = 11;
            break;
        case 'Q':
            card_number[order] = 12;
            break;
        case 'K':
            card_number[order] = 13;
            break;
        case '1':
            i++, card_number[order] = 10;
            break;
        default:
            card_number[order] = cina[i] - '0';
        }
        if(card_number[order] == 1 || card_number[order] == 10 || card_number[order] == 11 || card_number[order] == 12 || card_number[order] == 13)
            card[card_color[order]]++;
        if(card[1] == 5 || card[2] == 5 || card[3] == 5 || card[4] == 5)
            break;
        i++, order++;
    }
    if(order == 4) {\\注意是 4 的时候不用弃牌。
        printf("0\n");
        return 0;
    }
    for(i = 1; i <= 4; i++)
        if(card[i] == 5) break;
    for(j = 0; j < order; j++){//既然在 order 时弹出了,那么 order 所对应的牌一定是同花顺中的牌,所以 order 不需要遍历。
        if(card_color[j] == i && (card_number[j] == 1 || card_number[j] == 10 || card_number[j] == 11 || card_number[j] == 12 || card_number[j] == 13))
            continue;
        switch (card_color[j]) {
        case 1:
            printf("S");
            break;
        case 2:
            printf("H");
            break;
        case 3:
            printf("D");
            break;
        case 4:
            printf("C");
            break;
        }
        switch (card_number[j]) {
        case 1:
            printf("A");
            break;
        case 11:
            printf("J");
            break;
        case 12:
            printf("Q");
            break;
        case 13:
            printf("K");
            break;
        default:
            printf("%d", card_number[j]);
            break;
        }
    }
        printf("\n");
    return 0;
}

比较 第一次的代码运行记录 和 第二次的代码运行记录 ,可见运行时间和内存都有一定优化。