UVa 181 Hearts
2026/5/8 17:24:18 网站建设 项目流程

题目分析

Hearts \texttt{Hearts}Hearts是一种纸牌游戏,本题描述的是该游戏的简化版本。游戏使用一副标准的52 5252张扑克牌,包含4 44种花色(梅花C \texttt{C}C、方块D \texttt{D}D、红心H \texttt{H}H、黑桃S \texttt{S}S,按此顺序从小到大)和13 1313种点数(2 2210 1010J \texttt{J}JQ \texttt{Q}QK \texttt{K}KA \texttt{A}A,按此顺序从小到大)。

游戏规则要点:

  • 5 55名玩家(包括庄家),每人发10 1010张牌,剩余2 22张牌亮出,点数较大的那张牌的花色确定为将牌花色;若点数相同,则选择花色等级较高的作为将牌。
  • 游戏进行10 1010轮(称为“墩”)。每轮由一位玩家引牌,其余玩家按顺时针方向跟牌。第一轮由庄家左侧的玩家引牌,之后每轮由上一轮的赢家引牌。
  • 跟牌时,必须优先跟同花色;若无同花色,则必须出将牌(称为“将吃”);若无将牌,则垫牌(出任意牌)。
  • 每轮中,若有人出将牌,则出最大将牌者赢;否则出引牌花色最大者赢。
  • 计分:每轮结束后,赢家收取该轮的所有牌。整局结束后,统计每名玩家手中红心牌的点数之和(J = 11 \texttt{J}=11J=11Q = 12 \texttt{Q}=12Q=12K = 13 \texttt{K}=13K=13A = 14 \texttt{A}=14A=14)。

解题思路

本题要求模拟完整的游戏过程,输入包含多副牌(每副牌4 44行,每行若干张牌),输出5 55名玩家的最终得分,输出顺序为:庄家、庄家左侧、…、庄家右侧。

牌的数据表示

每张牌包含三个属性:

  • 字符串形式(如"TS"表示黑桃10 1010
  • 点数索引(0~12,对应2~A
  • 花色索引(0~3,对应C~S

发牌规则

发牌从庄家左侧玩家开始,顺时针每人一张,共发5 × 10 = 50 5 \times 10 = 505×10=50张。剩余两张牌确定将牌花色。

模拟每轮游戏

  1. 引牌:引牌者根据策略出牌 —— 从手牌中选出点数最大的牌;若点数相同且其中一张是将牌,则出将牌;否则出花色等级较高的牌。
  2. 跟牌:其余玩家依次出牌,策略为:
    • 优先出手牌中同花色点数最大的牌;
    • 若无同花色,则出将牌中点数最大的牌(将吃);
    • 若无将牌,则出手牌中点数最大的牌(垫牌)。
  3. 确定赢家:比较本轮的5 55张牌,规则同题述。
  4. 计分:赢家收取本轮所有牌,其中每张红心牌的点数累加到该玩家得分中。

数据结构

  • players5 55个向量,每个向量存储一名玩家的手牌。
  • scores5 55个整数,存储玩家得分。
  • 每轮结束后,赢家成为下一轮的引牌者。

输入处理

输入中一副牌分4 44行,每行包含若干张牌(每张牌由两个字符表示,如TS)。遇到单独一行#时终止输入。

关键实现细节

  • 发牌时,输入字符串需先合并再去除空白字符。
  • 剩余两张牌从输入末尾取得,注意字符串索引位置(前100 100100个字符对应50 5050张牌,剩余4 44个字符是最后两张牌)。
  • 排序比较函数需严格遵循题目中的策略。
  • 输出格式:每个分数占据3 33个字符宽度,右对齐,输出顺序为庄家、庄家左侧、庄家对面、庄家右侧、庄家左侧的左侧(即按4 → 0 → 1 → 2 → 3 4 \to 0 \to 1 \to 2 \to 340123的顺序)。

代码实现

// Hearts// UVa ID: 181// Verdict: Accepted// Submission Date: 2016-03-09// UVa Run Time: 0.000s//// 版权所有(C)2016,邱秋。metaphysis # yeah dot net#include<bits/stdc++.h>usingnamespacestd;structcard{string text;// 牌的字符串表示,如 "TS"intface;// 点数索引,0~12 对应 2~Aintsuit;// 花色索引,0~3 对应 C~S};vector<vector<card>>players;// 5 名玩家的手牌vector<card>followerCards;// 当前轮所有玩家出的牌vector<int>scores;// 5 名玩家的得分string faces="23456789TJQKA";// 点数顺序string suits="CDHS";// 花色顺序intleader,winner,trump;// 当前引牌者、当前赢家、将牌花色card winnerCard;// 当前轮中暂时领先的牌// 判断新出的牌是否能成为当前轮的赢家voidgetWinnerCard(intindex,card followerCard){if((followerCard.suit!=winnerCard.suit&&followerCard.suit==trump)||(followerCard.suit==winnerCard.suit&&followerCard.face>winnerCard.face)){winner=index;winnerCard=followerCard;}}// 跟牌时的排序规则:点数大的优先,点数相同时花色大的优先boolfollowerCmp(card x,card y){return(x.face!=y.face)?x.face>y.face:x.suit>y.suit;}// 获取某位玩家的跟牌cardgetFollowerCard(intindex,card leaderCard){card followerCard;// 按跟牌策略排序手牌sort(players[index].begin(),players[index].end(),followerCmp);// 1. 优先出同花色中的最大牌boolfound=false;for(inti=0;i<players[index].size();i++)if(players[index][i].suit==leaderCard.suit){followerCard=players[index][i];players[index].erase(players[index].begin()+i);found=true;break;}// 2. 若无同花色,则出将牌中的最大牌(将吃)if(found==false){for(inti=0;i<players[index].size();i++)if(players[index][i].suit==trump){followerCard=players[index][i];players[index].erase(players[index].begin()+i);found=true;break;}}// 3. 若既无同花色也无将牌,则垫出手牌中最大牌if(found==false){followerCard=players[index][0];players[index].erase(players[index].begin());}// 更新当前轮的赢家信息getWinnerCard(index,followerCard);returnfollowerCard;}// 引牌时的排序规则:点数大的优先;点数相同时,将牌优先;否则花色大的优先boolleaderCmp(card x,card y){if(x.face!=y.face)returnx.face>y.face;else{if(x.suit==trump&&y.suit!=trump)returntrue;if(x.suit!=trump&&y.suit==trump)returnfalse;returnx.suit>y.suit;}}// 获取引牌者的出牌cardgetLeaderCard(intindex){sort(players[index].begin(),players[index].end(),leaderCmp);card leaderCard=players[index][0];players[index].erase(players[index].begin());returnleaderCard;}// 模拟一轮游戏voidplay(){followerCards.clear();followerCards.push_back(getLeaderCard(leader));// 引牌者出牌winner=leader;winnerCard=followerCards.front();// 其余 4 名玩家依次跟牌for(inti=1;i<=4;i++){leader=(leader+1)%5;followerCards.push_back(getFollowerCard(leader,followerCards.front()));}// 赢家收取本轮所有牌,统计红心牌分数for(inti=0;i<followerCards.size();i++)if(followerCards[i].text[1]=='H')scores[winner]+=(followerCards[i].face+2);// face+2 得到实际点数leader=winner;// 下一轮由赢家引牌}intmain(intargc,char*argv[]){string line,deck;intlineNumber=0;// 初始化 5 名玩家的手牌和得分for(inti=1;i<=5;i++){vector<card>cards;players.push_back(cards);scores.push_back(0);}while(getline(cin,line),line!="#"){lineNumber++;deck+=line;// 合并多行输入// 每副牌共 4 行,读满后处理if(lineNumber==4){// 去除所有空白字符for(inti=deck.length()-1;i>=0;i--)if(isblank(deck[i]))deck.erase(deck.begin()+i);// 发牌:前 100 个字符对应 50 张牌,每人 10 张for(inti=0,j=0;i<deck.length()-4;i+=2,j++){string text=deck.substr(i,2);players[j%5].push_back((card){text,(int)faces.find(text[0]),(int)suits.find(text[1])});}// 剩余两张牌(第 51 张和第 52 张)确定将牌花色string last1=deck.substr(102,2);// 第 52 张string last2=deck.substr(100,2);// 第 51 张intface1=faces.find(last1[0]);intface2=faces.find(last2[0]);intsuit1=suits.find(last1[1]);intsuit2=suits.find(last2[1]);if(face1==face2)trump=suit1>suit2?suit1:suit2;// 点数相同,花色等级高的为将牌elsetrump=face1>face2?suit1:suit2;// 点数大的那张牌的花色为将牌// 重置得分fill(scores.begin(),scores.end(),0);// 第一轮由庄家左侧(索引0)引牌,共进行 10 轮leader=0;for(inti=1;i<=10;i++)play();// 输出结果:顺序为庄家(索引4)、庄家左侧(0)、...、庄家右侧(3)cout<<setw(3)<<right<<scores[4];for(inti=0;i<=3;i++)cout<<setw(3)<<right<<scores[i];cout<<endl;// 重置,准备处理下一副牌deck.clear();lineNumber=0;leader=0;}}return0;}

样例验证

输入样例:

TS QC 8S 8D QH 2D 3H KH 9H 2H TH KS KC 9D JH 7H JD 2S QS TD 2C 4H 5H AD 4D 5D 6D 4S 9S 5S 7S JS 8H 3D 8C 3S 4C 6S 9C AS 7C AH 6H KD JC 7D AC 5C TC QD 6C 3C #

输出:

22 0 68 0 14

与题目给出的输出一致,说明模拟正确。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询