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

拉马车游戏规则及实现

题目描述

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

游戏规则简述

假设有两个小朋友,分别是 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);
相关推荐
Catherinemin13 分钟前
剑指Offer|day4 LCR 004. 只出现一次的数字 II
javascript·算法
cdut_suye18 分钟前
深度优先搜索(DFS)与回溯法:从全排列到子集问题的决策树与剪枝优化
c++·人工智能·opencv·算法·决策树·深度优先·剪枝
CylMK39 分钟前
浅说单调队列
数据结构·c++·算法
天上飞的粉红小猪42 分钟前
动态规划-part1
算法·动态规划
IT猿手44 分钟前
强化学习Q-learning及其在机器人路径规划系统中的应用研究,matlab代码
开发语言·算法·matlab·机器人
2301_767992071 小时前
算法-动态数组-62.不同路径
数据结构·算法·leetcode
Solitudefire1 小时前
蓝桥杯刷题——day5
算法·蓝桥杯
arnold662 小时前
华为OD E卷(100分)21-传递悄悄话
算法·华为od·深度优先
mingchen_peng2 小时前
Prefix Decoder /Causal Decoder/Encoder-Decoder的区别
算法
thesky1234562 小时前
活着就好20241216
学习·算法