题解:P9381 [THUPC 2023 决赛] 那些脑海里最珍贵的
感受
比起群星连结,这题没有很多易错的细节(除了没仔细读题),写完马上就过了大样例,然后几乎过了,而且调试时不用专门输出游戏进程,题目要求的的输出已经够用了。
感谢出题人的大样例。
题意
两队回合制对战。
保证合法!
思路
框架
我们需要维护的是两个队伍中的人以及他们的各种信息。
除了题中提到的信息,我还维护了人的编号,位置,队伍和存活状态,可以顺便写一下更新函数(用于判断
struct weapon
{
string type;
double atk;
};
struct people
{
string race;
int id,pos,team = -1;
int alive,Lv,hp,maxhp,skillLv,passivesklLv;
double atk,def,atkbuff = 1,defbuff = 1,skillbuff = 1;
weapon wp;
void update()
{
if(hp > maxhp) hp = maxhp;
if(hp <= 0) hp = 0,alive = -1,passive(tim,0);
}
}a[N],b[N];
:::: 之后的攻击和主动技能等函数就可以直接在人的结构体里实现。
问题拆解
我们要完成的事有:
- 输入基本信息;
- 找当前行动的人和目标;
- 扣血;
- 普通攻击;
- 特殊攻击;
- 被动技能;
- 主动技能;
- 判断胜利;
- 输出。
实现
输入
使用
scanf可以读入特定格式内容,不用专门处理科学计数法,当成普通实数即可。 ::::info[setup]void setup() { cin>>n>>m; for(int i = 1; i <= n; i ++) { cin>>a[i].race; a[i].id = i,a[i].alive = 1; a[i].pos = position[i],a[i].team = 0; scanf(" Lv=%d maxhp=%d atk=%lf def=%lf skillLv=%d passivesklLv=%d",&a[i].Lv,&a[i].maxhp,&a[i].atk,&a[i].def,&a[i].skillLv,&a[i].passivesklLv); a[i].hp = a[i].maxhp; cin>>a[i].wp.type; scanf(" weaponatk=%lf",&a[i].wp.atk); } for(int i = 1; i <= m; i ++) { cin>>b[i].race; b[i].id = i,b[i].alive = 1; b[i].pos = position[i],b[i].team = 1; scanf(" Lv=%d maxhp=%d atk=%lf def=%lf skillLv=%d passivesklLv=%d",&b[i].Lv,&b[i].maxhp,&b[i].atk,&b[i].def,&b[i].skillLv,&b[i].passivesklLv); b[i].hp = b[i].maxhp; cin>>b[i].wp.type; scanf(" weaponatk=%lf",&b[i].wp.atk); } }::::
找当前行动的人和目标
注意要引用,不然会无法修改数组里的真实值。 ::::info[get_now,get_target]
people &get_target(int tarteam,int tarid) { if(tarteam) return b[tarid]; return a[tarid]; } people &get_now(int t,int last) { if(t & 1) { for(int i = last + 1; i <= n; i ++) if(a[i].alive == 1) return a[i]; for(int i = 1; i <= last; i ++) if(a[i].alive == 1) return a[i]; } for(int i = last + 1; i <= m; i ++) if(b[i].alive == 1) return b[i]; for(int i = 1; i <= last; i ++) if(b[i].alive == 1) return b[i]; }int last[2]; people &now = get_now(tim,last[tim & 1]); people &target = get_target(tim & 1,tar); last[tim & 1] = now.id;::::
扣血
计算被扣血人的防御,可以直接写在结构体里,记得更新存活状态。 ::::info[hurt]
void hurt(double strenth,int from) { if(alive != 1) return; double defense = def * defbuff; hp -= floor(strenth / defense); update(); printf("%s %d took %d damage from %s %d -> %d/%d\n",teams[team].c_str(),id,int(floor(strenth / defense)),teams[team ^ 1].c_str(),from,hp,maxhp); }::::
普通攻击
这里要实现的部分有点多,包括种族加成、方位加成。
直接调用目标的的扣血函数即可。 ::::info[basic]
double racebuff(people target)
{
if(race == target.race) return 1;
if(race == "Weak")
{
if(target.race == "Strong") return 1.1;
return 0.9;
}
if(race == "Average")
{
if(target.race == "Weak") return 1.1;
return 0.9;
}
if(target.race == "Weak") return 0.9;
return 1.1;
}
double posbuff(int atkpos,int ddgpos)
{
int x = (atkpos - ddgpos + 12) % 6;
if(!x) return 1.25;
if(x == 1 || x == 5) return 1;
if(x == 3) return 0;
return 0.75;
}
void basicattack(people &target,int atkpos,int ddgpos,double buff = 1)
{
double attack = atk * wp.atk * atkbuff * skillbuff * racebuff(target) * posbuff(atkpos,ddgpos) * buff;
target.hurt(attack,id);
}
::::
特殊攻击
对于「B」类型这里可以调用普通攻击,只要略加修改乘上一个加成即可。
对于另两种,我们要找到目标的左(西)右(东)两个人,类似于 get_now 函数。
::::info[get_left,get_right]
people &get_left(people now)
{
if(now.team)
{
int dis = 10,p = 0;
for(int i = 1; i <= m; i ++)
if(b[i].alive == 1 && b[i].pos < now.pos)
if(now.pos - b[i].pos < dis) dis = now.pos - b[i].pos,p = i;
return b[p];
}
int dis = 10,p = 0;
for(int i = 1; i <= n; i ++)
if(a[i].alive == 1 && a[i].pos < now.pos)
if(now.pos - a[i].pos < dis) dis = now.pos - a[i].pos,p = i;
return a[p];
}
people &get_right(people now)
{
if(now.team)
{
int dis = 10,p = 0;
for(int i = 1; i <= m; i ++)
if(b[i].alive == 1 && b[i].pos > now.pos)
if(b[i].pos - now.pos < dis) dis = b[i].pos - now.pos,p = i;
return b[p];
}
int dis = 10,p = 0;
for(int i = 1; i <= n; i ++)
if(a[i].alive == 1 && a[i].pos > now.pos)
if(a[i].pos - now.pos < dis) dis = a[i].pos - now.pos,p = i;
return a[p];
}
:::: 由此,我们又可以用普通攻击实现剩余的部分了。 ::::info[special]
void specialattack(people &target,int atkpos,int ddgpos)
{
if(wp.type == "B")
{
basicattack(target,atkpos,ddgpos,1.25);
}
else if(wp.type == "G")
{
int cnt = 1;
people &left = get_left(target);
if(left.alive == 1) cnt ++;
people &right = get_right(target);
if(right.alive == 1) cnt ++;
double x = 1.35 / cnt;
basicattack(target,atkpos,ddgpos,x);
basicattack(left,atkpos,ddgpos,x);
basicattack(right,atkpos,ddgpos,x);
}
else if(wp.type == "M")
{
basicattack(target,atkpos,ddgpos,1.15);
people &left = get_left(target);
basicattack(left,atkpos,ddgpos,0.23);
people &right = get_right(target);
basicattack(right,atkpos,ddgpos,0.23);
}
}
::::
被动技能
::::warning[注意区分「Weak 种族」的被动技能于其他两者的区别]{open} 「Weak 种族」的被动技能只在己方回合执行,而其他两者会随着人的死亡而改变,需要重新计算。 :::: 我使用了一个参数来确定是否计算「Weak 种族」的被动技能。 ::::info[passive]
void passive(int tim,bool wk = 1)
{
double we = 0,av = 0,st = 0;
for(int i = 1; i <= n; i ++)
if(a[i].alive == 1)
{
if(a[i].race == "Weak") we += weakpskill[a[i].passivesklLv];
else if(a[i].race == "Average") av += averagepskill[a[i].passivesklLv];
else if(a[i].race == "Strong") st += strongpskill[a[i].passivesklLv];
}
if(we > 0.05) we = 0.05;
if(av > 0.1) av = 0.1;
if(st > 0.1) st = 0.1;
for(int i = 1; i <= n; i ++)
if(a[i].alive == 1)
{
if(wk && (tim & 1) && we && a[i].hp != a[i].maxhp) printf("%s %d recovered +%d hp -> %d/%d\n",teams[0].c_str(),i,int(floor(we * a[i].maxhp)),min(a[i].hp + int(floor(we * a[i].maxhp)),a[i].maxhp),a[i].maxhp);
if(wk && tim & 1) a[i].hp += floor(we * a[i].maxhp);
a[i].defbuff = 1 + av;
a[i].atkbuff = 1 + st;
a[i].update();
}
we = 0,av = 0,st = 0;
for(int i = 1; i <= m; i ++)
if(b[i].alive == 1)
{
if(b[i].race == "Weak") we += weakpskill[b[i].passivesklLv];
else if(b[i].race == "Average") av += averagepskill[b[i].passivesklLv];
else if(b[i].race == "Strong") st += strongpskill[b[i].passivesklLv];
}
if(we > 0.05) we = 0.05;
if(av > 0.1) av = 0.1;
if(st > 0.1) st = 0.1;
for(int i = 1; i <= m; i ++)
if(b[i].alive == 1)
{
if(wk && !(tim & 1) && we && b[i].hp != b[i].maxhp) printf("%s %d recovered +%d hp -> %d/%d\n",teams[1].c_str(),i,int(floor(we * b[i].maxhp)),min(b[i].hp + int(floor(we * b[i].maxhp)),b[i].maxhp),b[i].maxhp);
if(wk && !(tim & 1)) b[i].hp += floor(we * b[i].maxhp);
b[i].defbuff = 1 + av;
b[i].atkbuff = 1 + st;
b[i].update();
}
}
::::
主动技能
因为主动技能可能的目标是己方或敌方,因此我传了两个目标。
需要注意的是「Average 种族」的主动技能是在己方三个回合结束时更新,存在一个全局数组里(因为是覆盖而非叠加所以不用 vector),另外加一个标记数组确定是否受到技能影响。
::::info[skill]
int hpdec[2][N][M],sta[2][N][M];
void skill(people &friendtarget,people &target)
{
if(race == "Weak")
{
double x = friendtarget.maxhp * weakskill[skillLv];
if(friendtarget.hp != friendtarget.maxhp) printf("%s %d recovered +%d hp -> %d/%d\n",teams[team].c_str(),friendtarget.id,int(floor(x)),min(int(floor(x + friendtarget.hp)),friendtarget.maxhp),friendtarget.maxhp);
friendtarget.hp += floor(x);
friendtarget.update();
}
else if(race == "Average")
{
double x = target.maxhp * averageskill[skillLv];
hpdec[target.team][target.id][tim] = floor(x);
hpdec[target.team][target.id][tim + 2] = floor(x);
hpdec[target.team][target.id][tim + 4] = floor(x);
sta[target.team][target.id][tim] = 1;
sta[target.team][target.id][tim + 2] = 1;
sta[target.team][target.id][tim + 4] = 1;
}
else if(race == "Strong")
{
double x = strongskill[skillLv];
friendtarget.skillbuff = x;
}
}
if(tim & 1)
{
for(int i = 1; i <= m; i ++)
{
if(b[i].alive != 1 || !sta[1][i][tim]) continue;
b[i].hp -= hpdec[1][i][tim];
b[i].update();
printf("%s %d took %d damage from skill -> %d/%d\n",teams[1].c_str(),i,hpdec[1][i][tim],b[i].hp,b[i].maxhp);
}
}
else
{
for(int i = 1; i <= n; i ++)
{
if(a[i].alive != 1 || !sta[0][i][tim]) continue;
a[i].hp -= hpdec[0][i][tim];
a[i].update();
printf("%s %d took %d damage from skill -> %d/%d\n",teams[0].c_str(),i,hpdec[0][i][tim],a[i].hp,a[i].maxhp);
}
}
::::
判断胜利
题目保证操作合法,因此只需在回合结束时判断即可。 ::::info[checkend]
void checkend()
{
int k = 1;
for(int i = 1; i <= n; k ++,i ++)
if(a[i].alive == 1) break;
if(k == n + 1) winner = "North";
k = 1;
for(int i = 1; i <= m; k ++,i ++)
if(b[i].alive == 1) break;
if(k == m + 1) winner = "South";
}
::::
输出
注意:
- 受伤无论有无实际扣血均输出。
- 「Weak 种族」的主动、被动技能均须判断是否有实际回血。
- 回合末的信息输出按照位置排列输出(不存在的人跳过,倒下的人也要输出
0 )。 - 最后若没有队伍「胜利」则不输出。(
其实这种错误只有我会犯)整合后的代码
::::warning[大模拟要锻炼实现、调试能力,不建议看下面的
丑陋代码]#include<bits/stdc++.h> using namespace std; const int printtim = 0,N = 20,M = 50010; int n,m,tim; string winner; string teams[] = {"South","North"}; double weakskill[] = {0,0.1,0.12,0.15,0.17,0.20},averageskill[] = {0,0.06,0.07,0.08,0.09,0.1},strongskill[] = {0,2.1,2.17,2.24,2.32,2.4}; double weakpskill[] = {0,0.013,0.016,0.019,0.022,0.025},averagepskill[] = {0,0.01,0.02,0.03,0.04,0.05},strongpskill[] = {0,0.01,0.02,0.03,0.04,0.05}; int position[] = {0,3,4,2,5,1,6},ps[] = {0,5,3,1,2,4,6}; struct people; people &get_left(people now); people &get_right(people now); void passive(int tim,bool wk); struct weapon { string type; double atk; }; int hpdec[2][N][M],sta[2][N][M]; struct people { string race; int id,pos,team = -1; int alive,Lv,hp,maxhp,skillLv,passivesklLv; double atk,def,atkbuff = 1,defbuff = 1,skillbuff = 1; weapon wp; void update() { if(hp > maxhp) hp = maxhp; if(hp <= 0) hp = 0,alive = -1,passive(tim,0); } double racebuff(people target) { if(race == target.race) return 1; if(race == "Weak") { if(target.race == "Strong") return 1.1; return 0.9; } if(race == "Average") { if(target.race == "Weak") return 1.1; return 0.9; } if(target.race == "Weak") return 0.9; return 1.1; } double posbuff(int atkpos,int ddgpos) { int x = (atkpos - ddgpos + 12) % 6; if(!x) return 1.25; if(x == 1 || x == 5) return 1; if(x == 3) return 0; return 0.75; } void hurt(double strenth,int from) { if(alive != 1) return; double defense = def * defbuff; hp -= floor(strenth / defense); update(); printf("%s %d took %d damage from %s %d -> %d/%d\n",teams[team].c_str(),id,int(floor(strenth / defense)),teams[team ^ 1].c_str(),from,hp,maxhp); } void basicattack(people &target,int atkpos,int ddgpos,double buff = 1) { double attack = atk * wp.atk * atkbuff * skillbuff * racebuff(target) * posbuff(atkpos,ddgpos) * buff; target.hurt(attack,id); } void specialattack(people &target,int atkpos,int ddgpos) { if(wp.type == "B") { basicattack(target,atkpos,ddgpos,1.25); } else if(wp.type == "G") { int cnt = 1; people &left = get_left(target); if(left.alive == 1) cnt ++; people &right = get_right(target); if(right.alive == 1) cnt ++; double x = 1.35 / cnt; basicattack(target,atkpos,ddgpos,x); basicattack(left,atkpos,ddgpos,x); basicattack(right,atkpos,ddgpos,x); } else if(wp.type == "M") { basicattack(target,atkpos,ddgpos,1.15); people &left = get_left(target); basicattack(left,atkpos,ddgpos,0.23); people &right = get_right(target); basicattack(right,atkpos,ddgpos,0.23); } } void skill(people &friendtarget,people &target) { if(race == "Weak") { double x = friendtarget.maxhp * weakskill[skillLv]; if(friendtarget.hp != friendtarget.maxhp) printf("%s %d recovered +%d hp -> %d/%d\n",teams[team].c_str(),friendtarget.id,int(floor(x)),min(int(floor(x + friendtarget.hp)),friendtarget.maxhp),friendtarget.maxhp); friendtarget.hp += floor(x); friendtarget.update(); } else if(race == "Average") { double x = target.maxhp * averageskill[skillLv]; hpdec[target.team][target.id][tim] = floor(x); hpdec[target.team][target.id][tim + 2] = floor(x); hpdec[target.team][target.id][tim + 4] = floor(x); sta[target.team][target.id][tim] = 1; sta[target.team][target.id][tim + 2] = 1; sta[target.team][target.id][tim + 4] = 1; } else if(race == "Strong") { double x = strongskill[skillLv]; friendtarget.skillbuff = x; } } }a[N],b[N]; people &get_left(people now) { if(now.team) { int dis = 10,p = 0; for(int i = 1; i <= m; i ++) if(b[i].alive == 1 && b[i].pos < now.pos) if(now.pos - b[i].pos < dis) dis = now.pos - b[i].pos,p = i; return b[p]; } int dis = 10,p = 0; for(int i = 1; i <= n; i ++) if(a[i].alive == 1 && a[i].pos < now.pos) if(now.pos - a[i].pos < dis) dis = now.pos - a[i].pos,p = i; return a[p]; } people &get_right(people now) { if(now.team) { int dis = 10,p = 0; for(int i = 1; i <= m; i ++) if(b[i].alive == 1 && b[i].pos > now.pos) if(b[i].pos - now.pos < dis) dis = b[i].pos - now.pos,p = i; return b[p]; } int dis = 10,p = 0; for(int i = 1; i <= n; i ++) if(a[i].alive == 1 && a[i].pos > now.pos) if(a[i].pos - now.pos < dis) dis = a[i].pos - now.pos,p = i; return a[p]; } void checkend() { int k = 1; for(int i = 1; i <= n; k ++,i ++) if(a[i].alive == 1) break; if(k == n + 1) winner = "North"; k = 1; for(int i = 1; i <= m; k ++,i ++) if(b[i].alive == 1) break; if(k == m + 1) winner = "South"; } people &get_target(int tarteam,int tarid) { if(tarteam) return b[tarid]; return a[tarid]; } people &get_now(int t,int last) { if(t & 1) { for(int i = last + 1; i <= n; i ++) if(a[i].alive == 1) return a[i]; for(int i = 1; i <= last; i ++) if(a[i].alive == 1) return a[i]; } for(int i = last + 1; i <= m; i ++) if(b[i].alive == 1) return b[i]; for(int i = 1; i <= last; i ++) if(b[i].alive == 1) return b[i]; } void setup() { cin>>n>>m; for(int i = 1; i <= n; i ++) { cin>>a[i].race; a[i].id = i,a[i].alive = 1; a[i].pos = position[i],a[i].team = 0; scanf(" Lv=%d maxhp=%d atk=%lf def=%lf skillLv=%d passivesklLv=%d",&a[i].Lv,&a[i].maxhp,&a[i].atk,&a[i].def,&a[i].skillLv,&a[i].passivesklLv); a[i].hp = a[i].maxhp; cin>>a[i].wp.type; scanf(" weaponatk=%lf",&a[i].wp.atk); } for(int i = 1; i <= m; i ++) { cin>>b[i].race; b[i].id = i,b[i].alive = 1; b[i].pos = position[i],b[i].team = 1; scanf(" Lv=%d maxhp=%d atk=%lf def=%lf skillLv=%d passivesklLv=%d",&b[i].Lv,&b[i].maxhp,&b[i].atk,&b[i].def,&b[i].skillLv,&b[i].passivesklLv); b[i].hp = b[i].maxhp; cin>>b[i].wp.type; scanf(" weaponatk=%lf",&b[i].wp.atk); } } void passive(int tim,bool wk = 1) { double we = 0,av = 0,st = 0; for(int i = 1; i <= n; i ++) if(a[i].alive == 1) { if(a[i].race == "Weak") we += weakpskill[a[i].passivesklLv]; else if(a[i].race == "Average") av += averagepskill[a[i].passivesklLv]; else if(a[i].race == "Strong") st += strongpskill[a[i].passivesklLv]; } if(we > 0.05) we = 0.05; if(av > 0.1) av = 0.1; if(st > 0.1) st = 0.1; for(int i = 1; i <= n; i ++) if(a[i].alive == 1) { if(wk && (tim & 1) && we && a[i].hp != a[i].maxhp) printf("%s %d recovered +%d hp -> %d/%d\n",teams[0].c_str(),i,int(floor(we * a[i].maxhp)),min(a[i].hp + int(floor(we * a[i].maxhp)),a[i].maxhp),a[i].maxhp); if(wk && tim & 1) a[i].hp += floor(we * a[i].maxhp); a[i].defbuff = 1 + av; a[i].atkbuff = 1 + st; a[i].update(); } we = 0,av = 0,st = 0; for(int i = 1; i <= m; i ++) if(b[i].alive == 1) { if(b[i].race == "Weak") we += weakpskill[b[i].passivesklLv]; else if(b[i].race == "Average") av += averagepskill[b[i].passivesklLv]; else if(b[i].race == "Strong") st += strongpskill[b[i].passivesklLv]; } if(we > 0.05) we = 0.05; if(av > 0.1) av = 0.1; if(st > 0.1) st = 0.1; for(int i = 1; i <= m; i ++) if(b[i].alive == 1) { if(wk && !(tim & 1) && we && b[i].hp != b[i].maxhp) printf("%s %d recovered +%d hp -> %d/%d\n",teams[1].c_str(),i,int(floor(we * b[i].maxhp)),min(b[i].hp + int(floor(we * b[i].maxhp)),b[i].maxhp),b[i].maxhp); if(wk && !(tim & 1)) b[i].hp += floor(we * b[i].maxhp); b[i].defbuff = 1 + av; b[i].atkbuff = 1 + st; b[i].update(); } } int last[2]; int main() { // freopen("2.in","r",stdin); // freopen("2.out","w",stdout); setup(); int t; cin>>t; for(tim = 1; tim <= t; tim ++) { if(printtim) cout<<tim<<" "; passive(tim); string s; int tar; cin>>s; scanf(" target=%d",&tar); people &now = get_now(tim,last[tim & 1]); people &target = get_target(tim & 1,tar); if(s == "Basicattack") { int atkpos,ddgpos; scanf(" atkpos=%d ddgpos=%d",&atkpos,&ddgpos); now.basicattack(target,atkpos,ddgpos); now.skillbuff = 1; } else if(s == "Specialattack") { int atkpos,ddgpos; scanf(" atkpos=%d ddgpos=%d",&atkpos,&ddgpos); now.specialattack(target,atkpos,ddgpos); now.skillbuff = 1; } else if(s == "Skill") { people &friendtarget = get_target((tim & 1) ^ 1,tar); printf("%s %d applied %s skill to ",teams[(tim & 1) ^ 1].c_str(),now.id,now.race.c_str()); if(now.race == "Average") printf("%s %d\n",teams[tim & 1].c_str(),target.id); else printf("%s %d\n",teams[(tim & 1) ^ 1].c_str(),friendtarget.id); now.skill(friendtarget,target); } last[tim & 1] = now.id; if(tim & 1) { for(int i = 1; i <= m; i ++) { if(b[i].alive != 1 || !sta[1][i][tim]) continue; b[i].hp -= hpdec[1][i][tim]; b[i].update(); printf("%s %d took %d damage from skill -> %d/%d\n",teams[1].c_str(),i,hpdec[1][i][tim],b[i].hp,b[i].maxhp); } } else { for(int i = 1; i <= n; i ++) { if(a[i].alive != 1 || !sta[0][i][tim]) continue; a[i].hp -= hpdec[0][i][tim]; a[i].update(); printf("%s %d took %d damage from skill -> %d/%d\n",teams[0].c_str(),i,hpdec[0][i][tim],a[i].hp,a[i].maxhp); } } printf("%s: ",teams[1].c_str()); for(int i = 1; i <= 6; i ++) if(~b[ps[i]].team) printf("%d/%d ",b[ps[i]].hp,b[ps[i]].maxhp); printf("\n%s: ",teams[0].c_str()); for(int i = 1; i <= 6; i ++) if(~a[ps[i]].team) printf("%d/%d ",a[ps[i]].hp,a[ps[i]].maxhp); puts("\n"); checkend(); } if(winner != "") printf("Team %s won.\n",winner.c_str());//若没有队伍「胜利」则不输出 return 0; }::::