蓝桥杯练习题-拉马车(中等)

拉马车游戏规则及实现

题目描述

小时候,我们可能玩过一种叫做"拉马车"的纸牌游戏。这个游戏规则简单,却非常吸引小朋友。

游戏规则简述

假设有两个小朋友,分别是 A 和 B。游戏开始时,他们各自持有一组随机的纸牌序列。游戏从 A 方开始,A 和 B 双方轮流出牌。

初始牌序列

  • A 方: [K, 8, X, K, A, 2, A, 9, 5, A]
  • B 方: [2, 7, K, 5, J, 5, Q, 6, K, 4]

其中 X 表示 "10",我们忽略纸牌的花色。

出牌规则

  1. 当轮到某一方出牌时,他从自己的纸牌队列的头部拿走一张,放到桌上,并且压在最上面一张纸牌上(如果有的话)。
  2. 如果出的牌与桌上已有的牌相同,那么可以将包括这张牌在内的,以及两张相同牌之间的所有纸牌赢回,放入自己牌的队尾。放入牌的顺序是与桌上的顺序相反的。
  3. 赢牌的一方继续出牌。
  4. 当某一方出掉手里最后一张牌,但无法从桌面上赢取牌时,游戏结束。

示例游戏过程

  1. A 出 K, B 出 2, A 出 8, B 出 7, A 出 X, 此时桌上的序列为: K, 2, 8, 7, X
  2. B 出 K,与桌上的 K 相同,赢回 K, 2, 8, 7, X,此时双方手里牌为:
    • A 方: [K, A, 2, A, 9, 5, A]
    • B 方: [5, J, 5, Q, 6, K, 4, K, X, 7, 8, 2, K]
  3. B 出 5, A 出 K, B 出 J, A 出 A, B 出 5 赢牌,桌上序列为: 5, K, J, A, 5
  4. 最后双方手里牌为:
    • A 方: [2, A, 9, 5, A]
    • B 方: [Q, 6, K, 4, K, X, 7, 8, 2, K, 5, A, J, K, 5]

输入输出描述

输入描述

输入为两行,两个字符串,分别表示 A、B 双方初始手里的牌序列。输入的字符串长度不超过 30。

输出描述

输出为一行,一个字符串,表示 A 先出牌,最后赢的一方手里的牌序。如果游戏无法结束,输出 -1

输入输出样例

输入

复制代码
96J5A898QA
6278A7Q973

输出

复制代码
2J9A7QA6Q6889977

这个输出表示在游戏结束时,B 方手里的牌序为 2J9A7QA6Q6889977

用C++写的答案:

出牌操作频繁设置函数op

op函数步骤:

1.设置结束条件,玩家的牌长度=0

2.设置赢牌标识符=true

3.取玩家的第一张牌

4.根据是否赢牌,选择不同操作

如果出的牌在出牌串里有,即赢牌了,则

    • 1.添加出的牌到自己的末尾
    • 2.把出牌串中从下标为0到出牌字符的下标i的牌都放到自己的末尾
    • 3.从出牌串中移除那些赢的牌

如果没有赢牌

1.把出的牌放到z串的开头

2.设置赢牌标识符=false

主函数:

1.输入两个串A,B

2.设置A,B赢牌的标识符

无限循环游戏:

3.如果A赢牌了,则

1.A出牌,并用flagA记录A是否赢牌

2.设置结束条件,A长度=0,输出B串

3.设置flagB!=flagA,让两者不冲突

B赢牌相同操作

cpp 复制代码
#include <iostream>//C++都需要导入的库
#include <string>//因为会用到字符串操作函数,导入string库
using namespace std; //用名字空间避免同名函数误用
string A,B,C;//声明三个字符串A,B,C分别放A的牌,B的牌,桌上的牌

/**
 * 函数用于处理玩家出牌的逻辑
 * @param x 正在操作的玩家手中的牌
 * @param z 桌面上的牌
 * @return 操作是否成功,如果成功则返回true,表示牌被成功放置或赢取,否则返回false
 */
bool op(string &x, string &z) {
//bool返回true表示牌成功放置,如果没有牌,返回false
//string x表示玩家目前要出的牌,加&表示需要对原字符串进行操作
//string z表示目前桌面上出掉的牌,因为出牌和赢牌,桌面上出的牌会发生改变,所以也加&
	//先设置结束条件
    // 如果玩家手中没有牌,即字符串长度为0,则无法出牌,返回false
    if (x.length() == 0) return false;
    // ans来标记出牌操作是否成功,初始化操作成功的标志为true
    bool ans = true;
    
    //要出牌,先获取玩家手中最顶端的牌
    char front = x[0];
    
    // 在桌面上的牌即z字符串 中查找是否有与 玩家即将出的牌相同的牌
    long i=z.find(front);
    
    // 看已经出的牌z中如果有玩家要出的牌,那么玩家赢牌,赢牌条件
    if(i!=string::npos) { 
  //如果i不等于 没有找到返回的string::npos(这里没有前面的std,是因为前面using namespace std)
  //- A 方: `[K, 8, X, K, A, 2, A, 9, 5, A]`
  // B 方: `[2, 7, K, 5, J, 5, Q, 6, K, 4]`
//例:A 出 K, B 出 2, A 出 8, B 出 7, A 出 X,,此时桌上的序列为: 'X,7,8,2,K'
     //B 出 K,与桌上的 K 相同,即赢牌
     //1.把要出的牌放在自己牌的末尾,即B=[ 5, J, 5, Q, 6, K, 4,K]
        x.insert(x.end(), front);
        // 将玩家手中的这张牌添加到自己牌的末尾
//字符串插入insert,位置x.end()在x串的最后,牌为front

//2.把出牌序列'X,7,8,2,K'中的从下标为0开始到找到K的下标的牌从x串的末尾放入X中,赢回 `K, 2, 8, 7, X`,倒着放进去,此时双方手里牌为:
  // - A 方: `[K, A, 2, A, 9, 5, A]`
   //- B 方: `[5, J, 5, Q, 6, K, 4, K, X, 7, 8, 2, K]`
        // 将桌面上从0到找到的相同牌之间的所有牌添加到玩家牌的末尾  
//因为每次出牌都放在出牌串z的开头,所以赢牌是从下标为0开始到找到的子字符的下标
        for (int j = 0; j <= i; ++j) {
            x.insert(x.end(), z[j]);
        }


        // 3.从桌面上移除这些牌
        z.erase(0, i + 1); 
        //移除函数,0表示开始位置,i+1表示要移除的个数,因为0-i有i+1个字符
    } else {
        // 如果没有找到相同的牌,将这张牌放在桌面上
        //在z串的开始放入玩家要出的牌,即出的每张牌都是放在出牌串z的开头
        z.insert(z.begin(), front);
        
        // 操作不成功,设置标志为false,没有赢牌
        ans = false;
    }
    
    // 从玩家手中移除已经出掉的牌,即在x串开头的牌
    x.erase(x.begin());
    //赢牌同样需要删掉第一张牌,因为赢牌后第一张牌已经收回x串中,此时的第一张牌应该已经出掉了并赢回牌,不是有效牌了,需要删掉
    
    // 返回操作是否成功的标志,表示是否赢牌
    return ans;
}

// 主函数入口
int main(int argc, const char *argv[]) {
	//常用写法
    // 从标准输入读取玩家A和B的牌
    cin >> A >> B;

    // 初始化标志,表示玩家A能出牌
    bool flagA = true; 

    // 初始化标志,表示玩家B能出牌,初始为false,因为游戏从玩家A开始
    bool flagB = false; 

    // 使用无限循环来持续游戏,直到游戏结束
    while (1) {
        // 如果玩家A能出牌
        if (flagA) {
            // 调用op函数处理玩家A的出牌逻辑,A出牌,C是出牌字符串
            //如果A赢牌了,返回true,赢牌的继续出
            //如果A没有赢牌,返回flase
            flagA = op(A, C);
            
            // 如果玩家A没有牌了,输出玩家B的牌,游戏结束
            if (A.length() == 0) {
                cout << B << endl;
                break;
            }
            
            // 设置玩家B为下一个出牌的玩家
            //如果A赢牌了,flagA=true,那B还是不能出牌。flagB=false
            //如果A没有赢牌,flagA=false,那B可以出牌了,flagB=true
            flagB = !flagA;
        }

        // 如果玩家B能出牌
        if (flagB) {
            // 调用op函数处理玩家B的出牌逻辑
            flagB = op(B, C);
            
            // 如果玩家B没有牌了,输出玩家A的牌,游戏结束
            if (B.length() == 0) {
                cout << A << endl;
                break;
            }
            
            // 设置玩家A为下一个出牌的玩家
            flagA = !flagB;
        }
    }
    
    // 程序结束,返回0
    return 0;
}

我写的C语言代码:

问题:

1.如果是频繁的操作用函数写

2.如果涉及标识符,多个最好设置对应的标识符

3.会无限循环的写while(1)

4.逻辑没弄清楚,赢牌后原本出的牌也一样放到末尾

c 复制代码
#include <stdio.h>
#include <stdlib.h>


int main(int argc, char *argv[])
{
  //输入A,B的初始牌序
  char A[100],B[100];
  char chupai[300]={0};
  //错误: 出牌队列里面的数应该初始化为0,因为出牌队列不像A,B队列会直接输入一串字符
  int a,b=-1;
  int flag=0;
  //设置队列存储目前的已经出的排序
  int top=0,rear=0;
  //输入一串字符%s
	scanf("%s",A);
	scanf("%s",B);
	//注意:scanf输入两个字符串,用空格隔开即可
  //A,B轮流出牌,放入队列中
  //for(int i=0;i<200;i++)
  //{
 //错误;一直循环,用while即可
 while(1){
    //出牌是把出牌方的队列头部牌拿出一张 放入队列中
    //赢牌的一方继续出牌
    if(flag==0){//A赢
    //出牌操作频繁,可以设置函数写
      a++;//A出牌
      chupai[rear]=A[a];//把出的牌放入队列中
      rear++;
      b++;//B出牌
      flag=1;//表示B现在出牌
      //注意:flag设置有问题,一次来回是A,B的话,那只有B赢了,flag才可以=1
      chupai[rear]=B[b];//把B出的牌放入队列中
      rear++;
    } else{
      b++;//B出牌
      flag=1;//表示B现在出牌
      chupai[rear]=B[b];//把B出的牌放入队列中
      rear++;
      a++;//A出牌
      chupai[rear]=A[a];//把出的牌放入队列中
      rear++;
    }
      for(int j=0;j<rear+1;j++)
       //如果出现有牌跟 已经出的纸牌序列其中一张牌相同,则把包括K在内及两个数之间的纸牌都拿回来逆序放入队尾
      {
        if(chupai[rear]==chupai[j])
        {
          for(int g=rear;g<=j;g++)
          {
            if(flag==0)//A赢
            {
                a++;//把队列中的牌放入A中
                A[a]=chupai[g];
            }else{
                b++;//把队列中的牌放入A中
                B[b]=chupai[g];
            }
          }
        }
      }
     
     // if(a==-1)
       // {
        //  puts(B);//输出 计算游戏结束时,赢的一方手里的牌序
      //  }
      //  if(b==-1)
       // {
       //   puts(A);
     //   }
     //注意:对测字符串长度的函数不熟
     //可以用字符长度来看字符是否为空
     
  }
  return -1;
}

补充:字符串查找函数

命名空间里的string类的find方法

**用处:**找子字符串在字符串中的位置(下标从0开始)

  • 找到子字符串:返回子字符串在字符串中的位置
  • 没找到子字符串:返回 std::string::npos(一般用long类型)

例:

c 复制代码
z="Hellow";
front="l";
z.find(front);//在Hellow找到l,下标从0开始,在下标2处找到"l",返回2

front="f";//在Hellow里找不到f,返回std::string::npos
z.find(front);
相关推荐
江畔柳前堤28 分钟前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore2 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考3 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX4 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面4 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号5 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽5 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI5 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者7 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者7 小时前
【征程6】校准量化中HistogramObserver解析
算法