无技术含量的顺序检索
Crazy_13754 · · 题解
AT262:皇家同花顺
题目简述
输入一长串字符串,为一副牌的顺序。需要凑成一顺同花顺,输出凑成同花顺之前的牌,无牌则输出0。保证有解。
思路
由于题面中已经强调了只有一副牌,显然每张牌只能出现一次。那么,则每当 10 、 J 、 Q 、 K 、 A 出现的时候将该牌色(C 、 S 、 D 、 H)的计数器加一,则当某一牌色计数器为“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;
}
比较 第一次的代码运行记录 和 第二次的代码运行记录 ,可见运行时间和内存都有一定优化。