专栏文章

那一天的模拟 模拟起来♪

P5013题解参与者 23已保存评论 24

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
24 条
当前快照
1 份
快照标识符
@mios7sxa
此快照首次捕获于
2025/12/03 00:17
3 个月前
此快照最后确认于
2025/12/03 00:17
3 个月前
查看原文
重要置顶:写大模拟出错时顺着代码看一遍挺有用的/qiang
怎么是我最喜欢的大模拟,话说如果写这题前对牛牛规则非常熟悉的话那题目意思就可以很好地理解了,但是请勿将时间沉迷于玩乐(
建议像猪国杀或这题讲到人物以及每个人物独有的手牌都使用结构体来存储,而不要整一大堆数组还不好理解。同时注意到 T1×105T\le1\times10^5 且每局中最多只能赢 200200,所以无需 long long

存储数据

结构体存每个人的牌情况以及分数。
CPP
struct Node
{
	int sc,c[15],ans,est;
	char ch[15];
	int flag;
}a[N];
解释:sc 表示分数,c 表示牌的大小以及 ch 表示花色,flag 记录有无铁板,ans 计分,est 表示最大牌。
这个时候聪明的宝子就要问了,倍数和名字怎么不存啊?我们可以以输入的顺序给每个人一个标号,再以 map<string,int> mp 来存每个人的名字以及附带标号。
那么输入名字时:
CPP
for(int i=1;i<=n;i++)
{
	string str;cin>>str;
	mp[str]=i;
}
想要查询某个名字为 s 的人就可以直接 a[mp[s]]
再说倍数,给一种较为新颖的方式。倍数和分数息息相关,那就打个小表存倍数
CPP
int times[25]=
{1,1,1,1,1,1,1,2,2,2,3,10};
访问倍数就是 times[a[mp[s]].sc],当有炸弹时把 sc 设置为 11 就行。
以及为什么表示最大牌不记录花色,提供一种一个数记录数字和花色的方法,把数字乘上一百,个位来存储花色。这样数字比较可以优先,数字相同就比较个位花色。

输入牌类

输入每张牌是可能会存在 aAa10,请注意判断一下,而不是直接输入两个字符并将第二个减去零。字符长度会变化,以 string 输入。
在并上我最爱的三目运算符((
CPP
for(int j=1;j<=5;j++)
{
	cin>>ca[j];
	a[id].ch[j]=ca[j][0];
	a[id].c[j]=(ca[j].size()==2?((ca[j][1]=='A')?1:(ca[j][1]-'0')):10);
}

计算牌值

建议写函数里,不然难调。
后期比较的有:牛几、有无铁板、最大牌
先求出在没有铁板情况下的最大牌,在这同时可以用桶来记录各号牌的个数以及总和(后面有用):
CPP
for(int i=1;i<=10;i++) tong[i]=0;
for(int i=1;i<=5;i++)
{
	sum+=a[x].c[i];
	tong[a[x].c[i]]++;
	big=max(big,a[x].c[i]*100+('d'-a[x].ch[i]));
}
采取一个变量先记录下当前最大值,并判断有没有牌是四个的炸弹。
CPP
int old=big;
for(int i=1;i<=10;i++)
{
	if(tong[i]>=4)
	{
		a[x].est=i;
		return 11;
	}
}
然后就直接三重循环判断有没有加起来是整十数或者三个数相同的。求牛几就是 sum 减去这三个再取模十,但是牛牛就会被取模成 0,所以取模方式如下:
CPP
cnt=(cnt-1)%10+1;
非常重要的点:以牛几最大为核心,铁板为其次。 当最大分数不是铁板需要把 flag 调回 0,如果是铁板则需要把其调回 1 并且将最大值设为相同的那三个数
可能是铁板后,同牛时,最大值会比其他人不是铁板的最大值要小,请注意铁板优先。
CPP
for(int i=1;i<=5;i++) 
for(int j=i+1;j<=5;j++)
for(int k=j+1;k<=5;k++)
{
	int tot=a[x].c[i]+a[x].c[j]+a[x].c[k];
	if(tot%10==0)
	{
		int cnt=sum-tot;
		cnt=(cnt-1)%10+1;
		if(cnt>res)
		{
			res=cnt,big=old;
			a[x].flag=0;
		}
    }
    if(a[x].c[i]==a[x].c[j]&&a[x].c[j]==a[x].c[k])
	{
		int cnt=sum-tot;
		cnt=(cnt-1)%10+1;
		if(cnt>=res)
		{
			res=cnt,big=a[x].c[i];
			a[x].flag=1;
        }
	}
}
返回牛几并将最大值存储下来:
CPP
a[x].est=big;
return res;
在主函数中返回牛几,用 sc 存储。
CPP
a[id].sc=f(id);

比较输赢

我们在比较两者时,可以先把倍数求出来。当有铁板且无炸弹时需要乘倍,整点神秘操作:
CPP
int u=mp[s[i]],U=times[a[u].sc]*((a[u].flag&&a[u].sc!=11)+1);
int v=mp[s[j]],V=times[a[v].sc]*((a[v].flag&&a[v].sc!=11)+1);
U 表示前者倍数,V 表示后者倍数。
当两者牛几不一样时,不多说了。
CPP
if(a[u].sc>a[v].sc)
{
	a[u].ans+=10*U;
	a[v].ans-=10*U;
}
else if(a[u].sc<a[v].sc)
{
	a[u].ans-=10*V;
	a[v].ans+=10*V;
}
当两者相同时,然后我就调了一个下午。
重要的判断条件。以 win 表示正负性,1 表示赢,-1 表示输,后面有奇妙作用。
当第一个人赢,存在可能性:我有铁板你没有、两人都有或没有铁板但是我最大值比你大。
正负性记得判断倍数是赢家的。
又是神秘三目运算符((
CPP
int win=(((a[u].flag>a[v].flag)||(a[u].est>a[v].est&&a[u].flag==a[v].flag))?1:-1);
a[u].ans+=win*10*(win==1?U:V);
a[v].ans-=win*10*(win==1?U:V);

输出答案

CPP
for(int i=1;i<=n;i++)
{
	cout<<str[i]<<' '<<a[i].ans<<endl;
}

后期优化

在求牌值时我们通过三次循环来求解,可以用两次循环枚举另外两张进行优化。
乱写后(疑似错误)交了一发虽然过了好像也没那么快,那我不改了。

Code

CPP
#include <bits/stdc++.h>
#define endl "\n"

using namespace std;

const int N=1e6+5;

int t,n;

int times[25]=
{1,1,1,1,1,1,1,2,2,2,3,10};

struct Node
{
	int sc,c[15],ans,est;
	char ch[15];
	int flag;
}a[N];

map<string,int> mp;

int tong[15];

int f(int x)
{
	int sum=0,res=0,big=0;
	for(int i=1;i<=10;i++) tong[i]=0;
	for(int i=1;i<=5;i++)
	{
		sum+=a[x].c[i];
		tong[a[x].c[i]]++;
		big=max(big,a[x].c[i]*100+('d'-a[x].ch[i]));
	}
	int old=big;
	for(int i=1;i<=10;i++)
	{
		if(tong[i]>=4)
		{
			a[x].est=i;
			return 11;
		}
	}
	for(int i=1;i<=5;i++) 
	for(int j=i+1;j<=5;j++)
	for(int k=j+1;k<=5;k++)
	{
		int tot=a[x].c[i]+a[x].c[j]+a[x].c[k];
		if(tot%10==0)
		{
			int cnt=sum-tot;
			cnt=(cnt-1)%10+1;
			if(cnt>res)
			{
				res=cnt,big=old;
				a[x].flag=0;
			}
		}
		if(a[x].c[i]==a[x].c[j]&&a[x].c[j]==a[x].c[k])
		{
			int cnt=sum-tot;
			cnt=(cnt-1)%10+1;
			if(cnt>=res)
			{
				res=cnt,big=a[x].c[i];
				a[x].flag=1;
			}
		}
	}
	a[x].est=big;
	return res;
}

string str[N];

string s[5],ca[15];

signed main()
{
	cin>>t>>t>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>str[i];
		mp[str[i]]=i;
	}
	while(t--)
	{
		for(int i=1;i<=3;i++)
		{
			cin>>s[i];
			int id=mp[s[i]];
			for(int j=1;j<=5;j++)
			{
				cin>>ca[j];
				a[id].ch[j]=ca[j][0];
				a[id].c[j]=(ca[j].size()==2?((ca[j][1]=='A')?1:(ca[j][1]-'0')):10);
			}
			a[id].flag=0;
			a[id].sc=f(id);
		}
		for(int i=1;i<=3;i++)
		{
			for(int j=i+1;j<=3;j++)
			{
				int u=mp[s[i]],U=times[a[u].sc]*((a[u].flag&&a[u].sc!=11)+1);
				int v=mp[s[j]],V=times[a[v].sc]*((a[v].flag&&a[v].sc!=11)+1);
				if(a[u].sc>a[v].sc)
				{
					a[u].ans+=10*U;
					a[v].ans-=10*U;
				}
				else if(a[u].sc<a[v].sc)
				{
					a[u].ans-=10*V;
					a[v].ans+=10*V;
				}
				else
				{
					int win=(((a[u].flag>a[v].flag)||(a[u].est>a[v].est&&a[u].flag==a[v].flag))?1:-1);
					a[u].ans+=win*10*(win==1?U:V);
					a[v].ans-=win*10*(win==1?U:V);
				}
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		cout<<str[i]<<' '<<a[i].ans<<endl;
	}

	#define Pyrf_uqcat return
	#define is 0
	#define Lovely ;
	
	Pyrf_uqcat is Lovely

}
大模拟真可爱!!

评论

24 条评论,欢迎与作者交流。

正在加载评论...