题解 P5013 水の斗牛
题解 P5013 水の斗牛
校内的考试考到了这题,但是因为组题人数据错误没能过,在正确数据下过了,记录一下。
题意
模拟题,很简单,没什么好说的,按照题目来就行。
分析
梳理一下模拟题的流程:
- 确定牌型。
- 比较大小。
- 胜负计分。
我们按照上面的三步流程来分析一下这道题目。
1. 确定牌型
(1)炸弹
直接在
(2)牛牛
见下面的(3)牛,根据给出的大小关系可以直接将牛牛理解为牛十。
(3)牛
直接在
(4)无牛
枚举找不到满足的牛,记为牛零。
2. 比较大小
题目给出的关系中,牛牛大于牛九,牛一大于无牛,这就是为什么前文要将牛牛记为牛十,无牛记为牛零。
比较大小时先比较炸弹,再比较牛数,其次比较铁板,最后比较最大牌。
3. 胜负计分
直接根据题意,两两比较大小后判断胜负,再按照题目给出的规则更新得分即可。
模拟题具体实现就看代码吧,毕竟思路就是按照题面的顺序来。
代码
//the code is from chenjh
#include<iostream>
#include<unordered_map>
#include<string>
#include<utility>
#include<vector>
#define MAXN 100005
using namespace std;
const int pn=5;//每个人的手牌数量。
int id,T,n;
string nm[MAXN];//每位玩家的名字。
unordered_map<string,int> ud;//名字对应的玩家编号。
struct PLAYER{
pair<char,int> p[pn];//牌(花色,点数)
int mp;//最大的一张牌编号。
vector<int> sp;//特殊牌型:炸弹、牛牌的编号。
int ss,ssp;//牌点数之和,特殊牌型点数之和。
int niu(const int x)const{return x%10?x%10:10;}//获取牛数,如果为 0 即为牛牛。
void init(){
sp.clear();
mp=ss=ssp=0;
for(int i=0;i<pn;i++) ss+=p[i].second;//手牌点数之和。
for(int i=1;i<pn;i++)
if(p[i].second>p[mp].second||(p[i].second==p[mp].second&&p[i].first<p[mp].first)) mp=i;//获取最大牌。
for(int i=0;i<pn;i++)for(int j=i+1;j<pn;j++)if(p[i].second==p[j].second)
for(int k=j+1;k<pn;k++)if(p[i].second==p[k].second)
for(int l=k+1;l<pn;l++)if(p[i].second==p[l].second){sp={i,j,k,l};break;}//枚举寻找炸弹。
if(sp.empty())//如果没有找到炸弹就寻找牛。
for(int i=0;i<pn;i++)for(int j=i+1;j<pn;j++)for(int k=j+1;k<pn;k++)
if((!((p[i].second+p[j].second+p[k].second)%10)&&(sp.empty()||niu(ss-(p[i].second+p[j].second+p[k].second))>niu(ss-ssp)))||//牌的点数和为 10 的倍数。
((p[i].second==p[j].second&&p[i].second==p[k].second)&&(sp.empty()||niu(ss-(p[i].second+p[j].second+p[k].second))>=niu(ss-ssp)))){//三张相同牌且点数更优。(能够取等的原因是铁板有着更为优秀的性质)
sp={i,j,k},ssp=0;
for(const int x:sp) ssp+=p[x].second;
}
ssp=0;for(const int x:sp) ssp+=p[x].second;
}
int BM()const{return sp.size()==4?p[sp[0]].second:0;}//判断炸弹。
int FE()const{return sp.size()==3&&p[sp[0]].second==p[sp[1]].second&&p[sp[1]].second==p[sp[2]].second?p[sp[0]].second:0;}//判断铁板,即三张牌点数相等。
int CW()const{return sp.size()==3?niu(ss-ssp):0;}//牛的分数。
bool operator < (const PLAYER&B)const{//注意这里是重载的小于运算符。
if(B.BM()!=BM()) return BM()<B.BM();//炸弹点数更小。
else{
if(CW()!=B.CW()) return CW()<B.CW();//牛数不等判断牛数大小。
else if(CW()&&B.CW()&&FE()!=B.FE()) return FE()<B.FE();//有牛判断铁板。
else return p[mp].second<B.p[B.mp].second||(p[mp].second==B.p[B.mp].second&&p[mp].first>B.p[B.mp].first);//都没有比较最大的牌。
}
return 0;
}
}a[MAXN];
int b[MAXN];
void solve(){
vector<int> pl(3);//当前对局的三个人。
for(int i=0,x;i<3;i++){
string nnm;cin>>nnm;
x=ud[nnm];//获取到选手的编号。
for(int j=0;j<pn;j++){
cin>>nnm;//获取当前手牌。
a[x].p[j]=nnm.size()==3?make_pair(nnm[0],10):make_pair(nnm[0],nnm[1]=='A'?1:nnm[1]-'0');//牌长度为 3 即点数为 10,第 2 个字符为 A 即点数为 1.
}
a[x].init();//初始化玩家。
pl[i]=x;//记录对局选手编号。
}
for(int x=0;x<3;x++)for(int y=x+1;y<3;y++){//两两比较,计算分数得失。
int i=pl[x],j=pl[y],d=1;
if(a[i]<a[j]) swap(i,j);//交换后满足条件玩家 i 胜 j 负。
if(a[i].BM()) d=10;//有炸弹底分倍数为 10。
else{
if(a[i].CW()==10) d=3;//牛牛倍数为 3。
else if(7<=a[i].CW()&&a[i].CW()<=9) d=2;//牛七/牛八/牛九倍数为 2。
else if(a[i].CW()<=6) d=1;//牛六~牛一/无牛倍数为 1.
if(a[i].FE()) d<<=1;//单独计算铁板的翻倍。
}
b[i]+=d,b[j]-=d;//得失分数。
}
}
int main(){
ios::sync_with_stdio(false),cin.tie(nullptr);
cin>>id>>T>>n;
for(int i=1;i<=n;i++) cin>>nm[i],ud[nm[i]]=i;
while(T--) solve();
for(int i=1;i<=n;i++) cout<<nm[i]<<' '<<10*b[i]<<'\n';//乘上底分 10.
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通