【C语言 每日一题】SOJ 1003 Hit or Miss

这篇博客介绍了一款名为 Hit or Miss 的纸牌游戏,详细阐述了游戏规则,包括单人和多人模式。博主通过模拟游戏过程来解决是否能获胜的问题,并指出无需复杂算法,只需循环模拟即可。在代码实现部分,博主使用了自制的循环队列结构体,以不超过5000次循环为条件判断游戏是否结束。
摘要由CSDN通过智能技术生成

题意

题目连接:http://soj.sysu.edu.cn/1003

有纸牌游戏 Hit or Miss,游戏流程为:有一套叠好的纸牌(52 张,4 组,值为 1 - 13,花色在本游戏不重要)。
1. 玩家从 1 开始计数;
2. 如果当前计数与最顶端的纸牌数值相同,则扔掉最顶端纸牌,否则将最顶端的纸牌放入这叠纸牌最底部;
3. 计数加一,当计数超过 13 时,回到 1 重新计数。重复第二步;

该游戏也支持多名玩家,最开始只给第一名玩家一叠纸牌(52张)。
1. 玩家从上次自己计数停止处开始计数,最开始为 1;
2. 当玩家接受到纸牌时塞入自己维护的纸牌的最底部。
3. 如果当前计数与自己维护的纸牌最顶端的纸牌数值不同,将最顶端的纸牌放入这叠纸牌最底部,并回到第一步。否则,如果该玩家是最后一名玩家,则扔掉该纸牌,如果不是最后一名玩家,则将该纸牌传递给下一位玩家。数值相同的情况下,游戏都从下一位玩家开始;
4. 当所有牌都被扔了时,游戏胜利。

根据输入的玩家数量以及一套乱序纸牌,判断游戏是否能够胜利。如果能够游戏胜利,则给出结束时,每位玩家或传递或扔掉的最后一张纸牌的数值。否则输出 unwinnable。

题解

题目没有涉及复杂的算法,只要模拟这个游戏就好了……为每个玩家维护一个纸牌队列,然后循环就行。至于游戏是否能够胜利这点,我们只能以循环的次数决定了,当循环次数过大时,游戏结束。(我在提交时设置的次数为 5000,能够 AC)。

代码

代码这块也很朴实。不过我没有用到标准库提供的队列,自己粗暴地实现了一个循环队列来模拟每个玩家维护的纸牌队列~

#include <stdio.h>
#include <string.h>

#define CARD_MAX 52
#define CARD_CIRCULAR_MAX 53 // 循环队列的大小
#define MAX_TIMES 5000

// 循环队列,多用了一个空位
typedef struct deck {
  char head;
  char tail;
  char cards[CARD_CIRCULAR_MAX];
} deck;

int main() {

  int t, T, i, j, times;
  int players, removed;

  scanf("%d", &T);

  for (t = 1; t <= T; ++t) {
    removed = 0;

    scanf("%d", &players);
    deck cards[players];
    for (i = 0; i < players; ++i) {
      cards[i].head = 0;
      cards[i].tail = 0;
      memset(cards[i].cards, 0, sizeof(cards[i].cards));
    }

    // 第一位玩家的初始纸牌
    for (i = 0; i < CARD_MAX; ++i)
      scanf("%d", &(cards[0].cards[i]));

    cards[0].head = 0;
    cards[0].tail = i;

    short last_count[players];
    short last_discard[players];
    memset(last_count, 0, sizeof(last_count));
    memset(last_discard, 0, sizeof(last_discard));

    times = 0;
    // times 判断终止条件
    while (times++ < MAX_TIMES && removed != CARD_MAX) {
      for (i = 0; i < players && removed != CARD_MAX; ++i) {
        // 玩家没有纸牌(循环队列空的)
        if (cards[i].tail == cards[i].head) continue;
        last_count[i] = last_count[i] == 13 ? 1 : last_count[i] + 1;
        if (cards[i].cards[cards[i].head] == last_count[i]) {
          // 更新用户丢弃或传递的纸牌
          last_discard[i] = cards[i].cards[cards[i].head];
          if (i == players - 1) {
            removed++;
          } else {
            // 塞入下一个玩家纸牌底部
            cards[i + 1].cards[cards[i + 1].tail] = cards[i].cards[cards[i].head];
            cards[i + 1].tail = (cards[i + 1].tail + 1) % CARD_CIRCULAR_MAX;
          }
          // 当前玩家丢掉or传递纸牌
          cards[i].head = (cards[i].head + 1) % CARD_CIRCULAR_MAX;
        } else {
          // 当前玩家把当前纸牌塞入自己纸牌底部
          cards[i].cards[cards[i].tail] = cards[i].cards[cards[i].head];
          cards[i].head = (cards[i].head + 1) % CARD_CIRCULAR_MAX;
          cards[i].tail = (cards[i].tail + 1) % CARD_CIRCULAR_MAX;
        }
      }
    }

    printf("Case %d: ", t);
    if (removed == CARD_MAX) {
      printf("%d", last_discard[0]);
      for (i = 1; i < players; ++i)
        printf(" %d", last_discard[i]);
      printf("\n");
    } else
      printf("unwinnable\n");
  }
}

这次稍微用了下 struct!一开始我还用 -> 来取值,但其实是用 . 啦!结构体指针的话才用 ->

typedef struct a {
  int q;
} a;
a t;
a* tp;
printf("%d, %d, %d", t.q, tp->q, (*tp).q);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值