P5013 水の斗牛

Luogu 链接

题意

题目描述

现有 \(n\) 个人玩斗牛游戏,一共进行了 \(t\) 轮,每轮都只有 \(3\) 个人参加游戏,每个人的初始的分都为 \(0\)

斗牛的规则如下:

发牌

在每轮斗牛开始前,每位玩家都会拿到 \(5\) 张牌。

每张牌一定是一个 \(2\)\(10\) 的整数或 \(\texttt{A}\)(表示 \(1\))。

每张牌都有一个花色,分别用 \(\texttt{a}\)\(\texttt{b}\)\(\texttt{c}\)\(\texttt{d}\) 来表示。


牌型

\(5\) 张牌中,除了炸弹和无牛型牌,都分为两个部分:牛和分。

牛包括 \(3\) 张点数相同的牌(铁板)和 \(3\) 张点数之和为 \(10\) 的倍数的牌;分为剩下 \(2\) 张牌,一套牌的分即这 \(2\) 张牌点数之和模 \(10\) 的余数。

  • 炸弹:\(4\) 张点数相同的牌。
  • 牛牛:分恰好为 \(0\)
  • 牛:分不为 \(0\),分和牛数的数值相同。例如,如果一套牌的分为 \(5\),则被称为牛五。
  • 无牛:不存在 \(3\) 张牌,其点数相同或其点数之和为 \(10\) 的倍数。

牌型大小比较

首先比较牌型:

  • 炸弹 \(>\) 牛牛 \(>\) 牛九 \(>\) 牛八 \(>\) 牛七 \(>\) 牛六 \(>\) 牛五 \(>\) 牛四 \(>\) 牛三 \(>\) 牛二 \(>\) 牛一 \(>\) 无牛。

否则按如下规则来比较:

  • 双方都有炸弹时,炸弹点数更大一方的更大。
  • 只有一方有铁板时,有铁板的一方更大。
  • 双方都有铁板时,铁板点数更大的一方更大。
  • 双方都没有铁板或都无牛时,比较双方点数最大的牌,点数更大的一方更大;如果双方点数最大的牌点数相同,则花色更大的一方更大,花色大小为 \(\texttt{a}>\texttt{b}>\texttt{c}>\texttt{d}\)

计分

一局游戏的底分为 \(10\) 分,即每位玩家都会押 \(10\) 分。

对于其中 \(2\) 名玩家,我们在比较完双方的牌型后,选择牌型更大的一方的牌型来计算得失分数的翻倍,具体如下:

  • 炸弹:底分 \(\times 10\)
  • 牛牛:底分 \(\times 3\)
  • 牛七、牛八、牛九:底分 \(\times 2\)
  • 无牛、牛一至牛六:底分。
  • 铁板:计算完牌型得分后,在牌型得分的基础上 \(\times 2\)
  • 注意:铁板翻倍不与炸弹翻倍同时进行。

计分时,采用两两计分的方式。

具体的,对于三名玩家 A、B、C,先计算 A 和 B 之间的分数得失,再计算 A 和 C 之间的分数得失,最后计算 B 和 C 之间的分数得失。

假设所有玩家都会打出他们手上的牌中的最大牌型,求这 \(t\) 轮游戏结束后,每位玩家的得分。


输入格式

第一行三个整数 \(id\)\(t\)\(n\)\(id\) 表示测试点编号,若是样例则为 \(0\)

接下来 \(n\) 行,每行一个仅包括大小写字母和数字的长度少于 \(11\) 的字符串表示玩家名称。

接下来 \(t\) 组,每组数据 \(6\) 行,可分为 \(3\) 个相同的部分:

  • 第一行一个字符串,表示玩家名称。
  • 第二行五个字符串表示该玩家手中的手牌。每一个字符串的第一个字符表示花色,其余字符表示牌的大小。

输出格式

\(n\) 行,每行一个字符串和一个数字,中间用空格隔开,表示一名玩家和他的最终得分

请按玩家名称的输入顺序输出。

思路

约定

为避免讲述过程中的歧义,先规定以下变量的意义:

  • \(num_i\):第 \(i\) 张牌的点数。
  • \(id_i\):第 \(i\) 张牌的花色。\(\texttt{a}\)\(4\)\(\texttt{b}\)\(3\)\(\texttt{c}\)\(2\)\(\texttt{d}\)\(1\)
  • \(bomb\):炸弹的点数,没有则为 \(0\)
  • \(tieban\):铁板的点数,没有则为 \(0\)
  • \(fen\):牛数,牛牛为 \(10\),无牛为 \(0\)
  • \(max_{num}\)\(5\) 张牌中的最大点数。
  • \(max_{id}\):在点数为 \(max_{num}\) 的牌中的最大花色。
  • \(sum\)\(5\) 张牌的点数之和。
  • \(cnt_i\):点数为 \(i\) 的牌有几张。
  • \(name_i\):第 \(i\) 个人的名字。
  • \(score_i\):第 \(i\) 个人的分数。

人名

map 记录人名所对应的下标即可。


发牌

读入 \(5\) 个字符串。

首先处理 \(id_i\)

然后处理 \(num_i\)

详见程序。


处理牌型

首先遍历一遍,处理 \(cnt_i\)\(sum\)\(max_{num}\)\(max_{id}\)

然后遍历 \(cnt_i\),看是否存在 \(i\) 使得 \(cnt_i=4\),若存在,则 \(bomb\gets i\),并直接退出。

否则再遍历 \(cnt_i\),看是否存在 \(i\) 使得 \(cnt_i=3\),若存在,则 \(tieban\gets i\),并计算此时的 \(fen\)

若既不存在炸弹,也不存在铁板,则判断是否有牛:

  • 让有序对 \((i,j)\) 遍历所有状态,若存在有序对 \((i,j)\),使得 \(num_i+num_j\equiv sum\pmod {10}\),则有牛,并计算此时的 \(fen\)
  • 若此时的 \(fen\) 比之前的大,则 \(tieban\)\(0\),并更新 \(fen\)(因为优先比较 \(fen\),并且 \(fen\) 相同时铁板更优)。

计分

先比较 \(bomb\),如果有,则直接计算,并返回。

然后比较 \(fen\) 的大小,若 \(fen\) 不相同直接计算。

\(fen\) 相同,则比较 \(tieban\),若 \(tieban\) 不相同直接计算。

\(tieban\) 相同,则比较 \(max_{num}\);若 \(max_{num}\) 仍相同,则比较 \(max_{id}\)

详见程序。


输出

按照输入顺序依次输出 \(name_i\)\(score_i\) 即可。

程序

AC 记录

#include<cstdlib>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cstdio>
#include<iostream>
#include<vector>
#include<map>
#include<cmath>
#include<iomanip>
#include<string>
#include<stack>
#define re register
#define ll long long
#define ull unsigned long long
#define vl __int128
#define ld long double
#define LL 2e18
#define INT 1e9
#define INF 0x3f3f3f3f
#define lb(x) (x&(-x))
#ifdef __linux__
#define gc getchar_unlocked
#define pc putchar_unlocked
#else
#define gc _getchar_nolock
#define pc _putchar_nolock
#endif
int T=1;
using namespace std;
inline bool blank(const char x){return !(x^32)||!(x^10)||!(x^13)||!(x^9);}
template<typename Tp>inline void read(Tp &x){x=0;re bool z=true;re char a=gc();for(;!isdigit(a);a=gc())if(a=='-')z=false;for(;isdigit(a);a=gc())x=(x<<1)+(x<<3)+(a^48);x=(z?x:~x+1);}
inline void read(double &x){x=0.0;re bool z=true;re double y=0.1;re char a=gc();for(;!isdigit(a);a=gc())if(a=='-')z=false;for(;isdigit(a);a=gc())x=x*10+(a^48);if(a!='.')return x=z?x:-x,void();for(a=gc();isdigit(a);a=gc(),y/=10)x+=y*(a^48);x=(z?x:-x);}
inline void read(ld &x){x=0.0;re bool z=true;re ld y=0.1;re char a=gc();for(;!isdigit(a);a=gc())if(a=='-')z=false;for(;isdigit(a);a=gc())x=x*10+(a^48);if(a!='.')return x=z?x:-x,void();for(a=gc();isdigit(a);a=gc(),y/=10)x+=y*(a^48);x=(z?x:-x);}
inline void read(char &x){for(x=gc();blank(x)&&(x^-1);x=gc());}
inline void read(char *x){re char a=gc();for(;blank(a)&&(a^-1);a=gc());for(;!blank(a)&&(a^-1);a=gc())*x++=a;*x=0;}
inline void read(string &x){x="";re char a=gc();for(;blank(a)&&(a^-1);a=gc());for(;!blank(a)&&(a^-1);a=gc())x+=a;}
template<typename T,typename ...Tp>inline void read(T &x,Tp &...y){read(x),read(y...);}
template<typename T>inline void read(T *begin,T *end){re T *i;if(begin<end)for(i=begin;i<end;++i)read(*i);else for(i=begin-1;i>=end;--i)read(*i);}
template<typename Tp>inline void write(Tp x){if(!x)return pc(48),void();if(x<0)pc('-'),x=~x+1;re int len=0;re char tmp[64];for(;x;x/=10)tmp[++len]=x%10+48;while(len)pc(tmp[len--]);}
inline void write(const double x){re int a=6;re double b=x,c=b;if(b<0)pc('-'),b=-b,c=-c;re double y=5*powl(10,-a-1);b+=y,c+=y;re int len=0;re char tmp[64];if(b<1)pc(48);else for(;b>=1;b/=10)tmp[++len]=floor(b)-floor(b/10)*10+48;while(len)pc(tmp[len--]);pc('.');for(c*=10;a;a--,c*=10)pc(floor(c)-floor(c/10)*10+48);}
inline void write(const ld x){re int a=6;re ld b=x,c=b;if(b<0)pc('-'),b=-b,c=-c;re ld y=5*powl(10,-a-1);b+=y,c+=y;re int len=0;re char tmp[64];if(b<1)pc(48);else for(;b>=1;b/=10)tmp[++len]=floor(b)-floor(b/10)*10+48;while(len)pc(tmp[len--]);pc('.');for(c*=10;a;a--,c*=10)pc(floor(c)-floor(c/10)*10+48);}
inline void write(const pair<int,double>x){re int a=x.first;if(a<7){re double b=x.second,c=b;if(b<0)pc('-'),b=-b,c=-c;re double y=5*powl(10,-a-1);b+=y,c+=y;re int len=0;re char tmp[64];if(b<1)pc(48);else for(;b>=1;b/=10)tmp[++len]=floor(b)-floor(b/10)*10+48;while(len)pc(tmp[len--]);a&&(pc('.'));for(c*=10;a;a--,c*=10)pc(floor(c)-floor(c/10)*10+48);}else printf("%.*lf",a,x.second);}
inline void write(const pair<int,ld>x){re int a=x.first;if(a<7){re ld b=x.second,c=b;if(b<0)pc('-'),b=-b,c=-c;re ld y=5*powl(10,-a-1);b+=y,c+=y;re int len=0;re char tmp[64];if(b<1)pc(48);else for(;b>=1;b/=10)tmp[++len]=floor(b)-floor(b/10)*10+48;while(len)pc(tmp[len--]);a&&(pc('.'));for(c*=10;a;a--,c*=10)pc(floor(c)-floor(c/10)*10+48);}else printf("%.*Lf",a,x.second);}
inline void write(const char x){pc(x);}
inline void write(const bool x){pc(x?49:48);}
inline void write(char *x){fputs(x,stdout);}
inline void write(const char *x){fputs(x,stdout);}
inline void write(const string &x){fputs(x.c_str(),stdout);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x),write(y...);}
template<typename T>inline void write(T *begin,T *end,const char c=' '){re T *i;for(i=begin;i<end;++i)write(*i,c);}
template<typename T>inline void init(T *begin,T *end,const T& val=T()){re T* i;for(i=begin;i<end;++i)*i=val;}
template<typename T>inline T max(T *begin,T *end){re T *ans,*i;for(i=begin;i<end;++i)if(i==begin||*ans<*i)ans=i;return *ans;}
template<typename T>inline T min(T *begin,T *end){re T *ans,*i;for(i=begin;i<end;++i)if(i==begin||*i<*ans)ans=i;return *ans;}
template<typename T>inline T calc_sum(T *begin,T *end,const T& val=T()){re T ans=val,*i;for(i=begin;i<end;++i)ans+=*i;return ans;}
template<typename T>inline bool is_equal(T *begin,T *end,const T& val=T()){re T *i;for(i=begin;i<end;++i)if(*i!=val)return false;return true;}

ll mod=0;
const int MAXN=1e5;
const int N=MAXN+10;
//#define DEBUG
//#define more_text

struct card{
	int num[5],id[5];   //点数、花色 a->4 b->3 c->2 d->1 
	
	int bomb;           //炸弹的点数 0表示没有炸弹
	int tieban;         //铁板的点数 0表示没有铁板
	int fen;            //分的点数,即牛数 牛牛点数为10,无牛为0,其他照常 
	int max_num,max_id; //最大点数 最大点数中的最大花色 
	
	int sum;            //所有点数的和 
	int cnt[11];        //cnt[i]表示点数为i的牌有几张 
	
	void init(){
		sum=max_num=max_id=bomb=tieban=fen=0;
		for(int i=1;i<=10;++i)cnt[i]=0;
		
		//处理sum、cnt[]、max_num、max_id 
		for(int i=0;i<5;++i){
			sum+=num[i];
			++cnt[num[i]];
			if(max_num<num[i])max_num=num[i],max_id=id[i];
			else if(max_num==num[i]&&max_id<id[i])max_id=id[i];
		}
		
		//处理炸弹
		for(int i=1;i<=10;++i){
			if(cnt[i]==4){
				bomb=i;
				return;
			}
		}
		
		//处理铁板
		int tieban_fen=0; //如果有铁板其所对应的分 否则为0 
		for(int i=1;i<=10;++i){
			if(cnt[i]==3){
				tieban=i;
				tieban_fen=(sum-3*i)%10;
				if(tieban_fen==0)tieban_fen=10;
				break;
			}
		}
		
		//处理非铁板的牛 
		int normal_fen=0,tmp_fen; //忽略铁板的牛所对应的分 没有则为0
		for(int i=0;i<4;++i){
			for(int j=i+1;j<5;++j){
				if(sum%10==(num[i]+num[j])%10){
					tmp_fen=(num[i]+num[j])%10;
					if(tmp_fen==0)tmp_fen=10;
					normal_fen=max(normal_fen,tmp_fen);
				}
			}
		}
		
		if(tieban_fen>=normal_fen)fen=tieban_fen;
		else tieban=0,fen=normal_fen;
	}
};
inline void read(card &x){
	char s[5][4];
	read(s[0],s[1],s[2],s[3],s[4]);
	for(int i=0;i<5;++i){
		x.id[i]='e'-s[i][0];
		if(s[i][1]=='A')x.num[i]=1;
		else if(s[i][1]=='1'&&s[i][2]=='0')x.num[i]=10;
		else x.num[i]=s[i][1]-'0';
	}
}

int id,n;string name[N],str[3];card arr[N];

map<string,int> mp;int score[N];

void judge(int x,int y){
	//双方至少有一个 bomb 
	
	if(arr[x].bomb>arr[y].bomb){
		score[x]+=100,score[y]-=100;
		return;
	}
	if(arr[x].bomb<arr[y].bomb){
		score[x]-=100,score[y]+=100;
		return;
	}
	
	int times;
	
	if(arr[x].fen>arr[y].fen){
		if(arr[x].fen==10)times=3;
		else if(arr[x].fen>6)times=2;
		else times=1;
		if(arr[x].tieban)times*=2;
	}
	else if(arr[x].fen<arr[y].fen){
		if(arr[y].fen==10)times=-3;
		else if(arr[y].fen>6)times=-2;
		else times=-1;
		if(arr[y].tieban)times*=2;
	} //fen 不相同 
	else if(arr[x].tieban>arr[y].tieban){
		if(arr[x].fen==10)times=3;
		else if(arr[x].fen>6)times=2;
		else times=1;
		times*=2;
	}
	else if(arr[x].tieban<arr[y].tieban){
		if(arr[y].fen==10)times=-3;
		else if(arr[y].fen>6)times=-2;
		else times=-1;
		times*=2;
	} //fen 相同,但 tieban 不相同 
	else if(arr[x].max_num*10+arr[x].max_id>arr[y].max_num*10+arr[y].max_id){
		if(arr[x].fen==10)times=3;
		else if(arr[x].fen>6)times=2;
		else times=1;
		if(arr[x].tieban)times*=2;
	}
	else if(arr[x].max_num*10+arr[x].max_id<arr[y].max_num*10+arr[y].max_id){
		if(arr[y].fen==10)times=-3;
		else if(arr[y].fen>6)times=-2;
		else times=-1;
		if(arr[y].tieban)times*=2;
	} //fen 和 tieban 都相同 
	score[x]+=times*10,score[y]-=times*10;
}

void solve(int step){
	read(id,T,n);
	for(int i=1;i<=n;++i){
		read(name[i]);
		mp[name[i]]=i;
	}
	while(T--){
		for(int i=0;i<3;++i){
			read(str[i]);
			read(arr[mp[str[i]]]);
			arr[mp[str[i]]].init();
		}
		judge(mp[str[0]],mp[str[1]]);
		judge(mp[str[1]],mp[str[2]]);
		judge(mp[str[2]],mp[str[0]]); //两两比较 
	}
	for(int i=1;i<=n;++i)write(name[i],' ',score[i],'\n');
}
/*
Input:

Output:

Outline:

*/
int main(){
	#ifdef DEBUG
	freopen("test.in","r",stdin);freopen("test.out","w",stdout);
	#endif
	#ifdef more_text
	read(T);
	#endif 
	for(int i=0;i<T;++i)solve(i);
	#ifdef DEBUG
	fclose(stdin);fclose(stdout);
	#endif
	return 0;
}
posted @ 2025-03-09 10:08  LXcjh4998  阅读(65)  评论(0)    收藏  举报