题目:

思路:

27张牌,分成3行,每行9张。对于这随机生成的27张牌,每个牌设置一个属性cv:被选中的次数。
初始27张牌每张牌的被选中次数为 0 ;

第一***作:
三行中被选中行的牌的次数+1,此时有9张牌的cv是1,28张牌的cv是0

第二***作:
将cv为1的牌平均分成3行,剩下的随意放置,再次让用户选择,此时被选中的行的牌的次数+1,此时有3张牌的cv是2,其余牌的cv<2。

第三***作:
将3张cv是2的牌平均分成3行,其余的随意放置,再次让用户选择,此时被选中的行的牌的次数+1,此时有1张牌的cv是3,其余牌的cv<3。这张牌就是用户选中的牌。

代码:

#include <stdio.h>
#include <time.h>
#include<stdlib.h>

struct poker{
    int val ; // 1(A),2,3,4,5,6,7,8,9,10,11(J),12(Q),13(K)
    int kind ; // 0 , 1 , 2 , 3
    int is_joker ; // 0不是王, 1小王,2大王
    int cv ; // 被选中次数
};

const char kind_name[5][20] = { " ", "Spade", "Heart", "Diamond", "Club" } ; // 黑,红,片,花
const char val_name[20][3] = { " ", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" } ;
int history_num = 0 ; // 一共玩了多少轮游戏
int pos[4][20] ; // 此次输出每个位置的扑克是哪一张
poker history[100] ; // 记录每次游戏的选择
poker card[27] ; // 被选出的27张扑克
poker card_list[54] ; // 所有54张扑克

void printPoker( struct poker p ) { // 输出扑克p信息
    if ( p.is_joker == 1 ) { // 是小王
        printf( "joker 1 " ) ;
    }
    else if ( p.is_joker == 2 ) { // 是大王
        printf( "JOKER 2 " ) ;
    }
    else { // 其他牌
        printf("%s %s ", kind_name[p.kind], val_name[p.val] ) ;
    }
}
void outInfermation() { // 输出个人信息
    int T=100;
    while ( T-- ) { // 输出上边框
        printf( "*" ) ;
    }
    printf("\n姓名:%s\t学号:%s\t班级:%s\t完成时间:", "小明","222018????","通信工程一班" ) ;
    time_t timep;   time (&timep);  printf("%s\n",ctime(&timep)); // 输出当前时间
    T = 100 ;
    while ( T-- ) { // 输出下边框
        printf( "*" ) ;
    }
    printf( "\n" ) ;
}
void outMenu() { // 输出功能菜单
    printf( "\n\n\n\*****************游戏菜单*********************************\n" ) ;
    printf( "1.开始游戏\n" ) ;
    printf( "2.查看历史记录\n" ) ;
    printf( "3.退出游戏\n" ) ;
    printf( "**********************************************************\n\n" ) ;
}
void outHistory() { // 输出历史记录(每次游戏用户选中的牌)
    int i;
    if ( history_num == 0 ) {
        printf( "历史记录为空\n" ) ;
    }
    for (i=0; i<history_num; i++ ) {
        printf( "第%d次游戏玩家选择的扑克是:", i+1 ) ;
        printPoker( history[i] ) ;
        printf( "\n" ) ;
    }
}
void Init() { // 初始化信息
    history_num = 0 ; // 历史记录清零
    int i, j, n=0 ;
    for (i=1; i<=4; i++) { // 为card_list数组的52张牌初始化
        for (j=1; j<=13; j++) {
            card_list[n].val = j ;
            card_list[n].kind = i ;
            card_list[n].is_joker = 0 ;
            card_list[n].cv = 0 ;
            n++ ;
        }
    }
    card_list[n].cv = 0 ;   card_list[n++].is_joker = 1 ; // 小王
    card_list[n].cv = 0 ;   card_list[n++].is_joker = 2 ; // 大王
    for(i=0;i<54;i++){
        printPoker( card_list[i] ) ;
    }
}
void creatCardList() { // 随机从54张牌中选出27张,被选出的牌存在card数组中
    //每次选择的策略为:随机生成一个数字x,从当前余剩的牌中,数到第x张牌,就是此次被选中的牌。重复27次即可
    int vis[54] = {0}, i, j, rest = 54 ;
    for (i=0; i<27; i++) { // 进行27次选择
        int pos = rand()%rest, now = 0 ;
        for (j=0; j<54; j++) {
            if ( now == pos && vis[j]==0 ) { // 数到了第pos张牌
                break ;
            }
            if ( vis[j] == 0 ) { // 该牌未被选择
                now++ ;
            }
        }
        vis[j] = 1 ; // 标记次张牌已被选中
        card[i] = card_list[j] ; // 把这张牌放入选中的27张牌中
        rest-- ;
    }
}
poker getCard( int turn , int num ) { // 猜用户选中的扑克牌(turn 表示是第几轮猜牌,num表示这一轮每行需要放置多少张cv值为turn-1的牌 )
    int i, j, k=0 ;
    printf( "第%d轮游戏开始!\n", history_num+1 ) ;
    printf( "第%d波筛选:\n" , turn ) ;

    ///按照规则放置扑克(3行,每行只能放num张choose_cnt值为turn-1的牌)
    int vis[27] = {0} ; // vis[i]表示第i张牌是否已经为其分配位置

    for (i=1; i<=3; i++) {
        int rest = num , rest2 = 9-num ; // 当前行还需要放rest张cv值为turn-1的牌,还需要放rest2张cv值小于turn-1的牌
        k = 0 ; // k表示当前第i行已经放了多少张牌
        for (j=0; j<27; j++) { // 考察第j张牌是否可以放入第i行
            if ( vis[j] == 1 ) continue ;
            if ( card[j].cv == turn-1 ) { // vc==true-1的牌
                if ( rest>0 ) {
                    k++ ;
                    pos[i][k] = j ;
                    vis[j] = 1 ;
                    rest--;
                }
            }
            else { // vc<true-1的牌
                if ( rest2>0 ) {
                    k++;
                    pos[i][k] = j ;
                    vis[j] = 1 ;
                    rest2--;
                }
            }
        }
    }

    ///输出摆放情况并等待用户选择
    printf( "请从下列扑克中选出一张,并输入它的所在行数\n" ) ;
    for (i=1; i<=3; i++) {
        printf( "# line %d : ", i ) ;
        for (j=1; j<=9; j++) {
            printPoker( card[pos[i][j]] ) ;
            printf(" ");
        }
        printf("\n");
    }
    int choose_line = 0 ;
    scanf( "%d", &choose_line ) ;

    /// 标记被选中的行数的扑克
    for (i=1; i<=9; i++) {
        card[ pos[choose_line][i] ].cv++ ;
    }

    ///返回答案
    if ( turn == 3 ) { // 如果是最后一轮就返回标记次数为3的唯一一张牌
        for (i=0; i<27; i++) {
            if ( card[i].cv == 3 ) return card[i] ;
        }
    }
    else { // 否则继续进行下一轮游戏
        return getCard( turn+1 , num/3 ) ;
    }
}
poker playGame() { // 玩游戏
    creatCardList(); // 随机选出27个卡片
    struct poker ret ;
    ret = getCard( 1 , 9 ) ; // 进行第一轮游戏
    return ret ;
}

int main()
{
    outInfermation(); // 输出个人信息
    Init(); // 扑克信息初始化
    int op = 0 ;
    while ( op != 3 ) {
        outMenu(); // 输出功能菜单
        scanf( "%d", &op ) ;
        if ( op == 1 ) { // 开始游戏
            history[history_num] = playGame() ;
            printf("游戏结束:\n你选择的扑克是:");
            printPoker( history[history_num] ) ;
            printf( "\n" ) ;
            history_num++ ;
        }
        else if ( op == 2 ) { // 输出历史游戏信息
            outHistory();
        }
        else if ( op == 3 ) { // 退出游戏
            return 0 ;
        }
        else { // 输入不合法
            printf( "输入功能标号不合法,请重新输入:\n" ) ;
        }
    }
    return 0;
}

运行截图: