题解:P7506 「Wdsr-2.5」琪露诺的算数游戏
思路
大模拟,打法暴力。单独考虑普通牌和解牌。
对于普通牌,因为只有三张手牌,所以我们可以在每次 选择出牌时都遍历一遍,找到最大或最小的合法牌。
对于解牌,只有三种,可以暴力搞。按优先级遍历三遍,分别找是否有对应的牌,如果有直接出就好。
比较难处理的是 DOUBLE 标记,其实标记只会传给下一个人,我们只要记录标记是否传递着。如果用解牌跳过,那么标记仍在传递。如果没有解牌只能用普通牌,这时无论输赢,标记都会被消除。
注意事项
-
出牌的优先级在有 DOUBLE 标记和没有时不一样, 没有标记时为乘法牌、加法牌、减法牌、除法牌、固定牌,尽可能大,有标记时为除法牌、减法牌、加法牌、乘法牌、固定牌,尽可能小,我们可以预处理优先级。
-
如果有标记时要优先出解牌,没有标记优先出普通牌。DOUBLE 标记如果用解牌消掉直接跳下一个人,否则还要按没有标记的再出一次牌。
-
出牌顺序每次重置。
-
如果输了要重新摸三张牌。
-
负数向下取整的问题,在这道题中因为只有除二的操作,所以我们可以直接用右移一代替。
-
建议多写函数,把各个函数调好了再整合。
参考代码
注释很详细。
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+5;
int n,m,k,now,st,p,fl=1;
string d[N];
bool dou;
map<char,int> mp[2]={{{'C',1},{'A',2},{'B',3},{'D',4},{'E',5}},{{'D',1},{'B',2},{'A',3},{'C',4},{'E',5}}};
//注意优先级
struct P
{
string name;
string c[3];
} a[35];
int cal(int x,string c)
{
int len=c.length(),tmp=0;
if(len==2) tmp=c[1]-48;
else tmp=(c[1]-48)*10+c[2]-48;
if(c[0]=='A') return x+tmp;
else if(c[0]=='B') return x-tmp;
else if(c[0]=='C') return x*2;
else if(c[0]=='D'&&c[1]!='O') return x>>=1;//和 DOUBLE 区分,注意向下取整
else if(c[0]=='E') return tmp;
else return 10000000;// 单独考虑解牌,这里不计
}
bool jd(int i)//暴力搞
{
for(int j=0;j<3;j++) if(a[i].c[j]=="PASS")
{
cout<<a[i].name<<" used "<<a[i].c[j]<<",now p="<<p<<".\n";
a[i].c[j]=d[++now]; return 1;
}
for(int j=0;j<3;j++) if(a[i].c[j]=="TURN")
{
cout<<a[i].name<<" used "<<a[i].c[j]<<",now p="<<p<<".\n";
a[i].c[j]=d[++now];fl*=-1; return 1;
}
for(int j=0;j<3;j++) if(a[i].c[j]=="DOUBLE")
{
cout<<a[i].name<<" used "<<a[i].c[j]<<",now p="<<p<<".\n";
a[i].c[j]=d[++now]; dou=1; return 1;
}
return 0;
}
pair<int,int> work(int i,bool op)
{
int tm,id;
if(op==0)
{
tm=-1e9,id=-1;
for(int j=0;j<3;j++)
{
int tmp=cal(p,a[i].c[j]);
if(tmp>99) continue;
else if(tmp>tm) tm=tmp,id=j;//较大的
else if(tmp==tm)
{
if(mp[op][a[i].c[id][0]]>mp[op][a[i].c[j][0]]) id=j;
}
}
}
else
{
tm=1e9,id=-1;
for(int j=0;j<3;j++)
{
int tmp=cal(p,a[i].c[j]);
if(tmp>99) continue;
else if(tmp<tm) tm=tmp,id=j;//较小的
else if(tmp==tm)
{
if(mp[op][a[i].c[id][0]]>mp[op][a[i].c[j][0]]) id=j;
}
}
}
return make_pair(tm,id);
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
cin>>a[i].name>>a[i].c[0]>>a[i].c[1]>>a[i].c[2];
}
for(int i=1;i<=k;i++) cin>>d[i];
st=1;
for(int g=1;g<=m;g++)
{
printf("Round %d:\n",g);
p=0; fl=1;//重置
for(int i=st;;i+=fl)
{
if(i==n+1) i=1;
if(i==0) i=n;
if(dou)
{
if(!jd(i))//先判解牌
{
dou=0;//没有解牌,消除标记
pair<int,int> pii=work(i,1);
int id=pii.second,tm=pii.first;
if(id!=-1)
{
p=tm;
cout<<a[i].name<<" used "<<a[i].c[id]<<",now p="<<p<<".\n";
a[i].c[id]=d[++now];
}
else
{
if(jd(i)) continue;
cout<<a[i].name<<" lost the game.\n";
for(int j=0;j<3;j++) a[i].c[j]=d[++now];
st=i; break;
}
}
else continue;//注意直接跳
}
pair<int,int> pii=work(i,0);
int id=pii.second,tm=pii.first;
if(id!=-1)
{
p=tm;
cout<<a[i].name<<" used "<<a[i].c[id]<<",now p="<<p<<".\n";
a[i].c[id]=d[++now];
}
else
{
if(jd(i)) continue;//判解牌
cout<<a[i].name<<" lost the game.\n";
for(int j=0;j<3;j++) a[i].c[j]=d[++now];
st=i; break;
}
}
}
return 0;
}