题解()
2026-02-28 13:57:46
发布于:湖南
中还给我们提供了一个非常方便的容器—— 。对于这种偏工程向的题目,显然,用List与迭代器实现会更加简单,直观,好写。
$ps$ :考虑到题目中对于每个对象所需存储的信息并不多,我便使用了更快的 $Struct$ 来实现我的代码。
读题 分析题目需求
首先,我们仔细地阅读一遍题目,首先,对于地图,我们发现对于地图上的一个点,我们需要储存两种信息——能否通过,与信息素数量。我们这么定义地图上的每一个点:
struct mpNode
{
int message=0;
bool canMove=true;
};
mpNode mp[MAXN][MAXN];
同样地,我们发现,对于每一只蚂蚁,我们都只需要储存它们的年龄,等级,血量,当前位置,上一秒的位置,血量最大值,是否拥有蛋糕就可以了。为了能够更好地创建一个蚂蚁个体,我们手动写一个构造函数:
struct antNode
{
int age;
int level;
int HP;
int posx;
int posy;
int prex;
int prey;
int maxHP;
bool isCake;
antNode(int age,int level,int HP,int x,int y,bool isCake=false):
age(age),level(level),HP(HP),posx(x),posy(y),isCake(isCake),prex(x),prey(y),maxHP(HP){}
};
考虑蚂蚁的存储方式,我们发现,在游戏的所有过程中,蚂蚁的行动顺序都是一样的,从年龄最大的到年龄最小的。于是,我们想到了使用Set来维护我们的蚂蚁序列。但是,在STL中的Set是具有只读属性的,不能对其中元素进行修改。我们再仔细思考生成蚂蚁的过程,发现我们只需要维护一个蚂蚁的链表即可。为什么呢?因为蚂蚁的最大数量是固定的,对于后生成的蚂蚁,我们只需要把它们放到链表的最后,对于每个过程,只需要从前往后遍历链表,就可以实现了。于是,我们考虑使用STL的List
(如果你并不会使用List,可以去自行百度地说,操作很少)
list<antNode> antList;
那么,既然我们使用了 $List$ 来维护蚂蚁序列,对于单个防御塔,我们就可以把攻击的对象以迭代器的方式储存起来,实现更直观,更简便的攻击操作了。
struct towerNode
{
int x;
int y;
double minDis;
bool haveTarget;
bool haveCakeTarget;
list<antNode>::iterator target;
};
考虑全局变量,对于蚂蚁生成,我们需要记录一共生成了多少只蚂蚁,而由于对 $List$ 的求 $Size$ 操作是 $O(n)$ 的,作为一个合格的 $OIer$ ,我们自然要选择更快的方法,于是我们再用一个全局变量储存当前存活了多少只蚂蚁。对于转向操作,我们需要从向东开始,储存一个顺时针旋转的方向数组,对于取蛋糕操作,我们还需要记录蛋糕是否已经被取走。重新考虑蚂蚁生成操作,我们不难发现,在一局游戏中,蚂蚁的等级是拥有最大值的,那么,很显然,在给定了 $t$ 之后,$1.1 k$
是可以预处理出来的,我们也用全局变量将它储存起来。
const double eps=1e-10;
const int MAXN=10;
int n,m,s,d,t;
double r;
int antCnt;
int nowt;
int dirx[4]={0,1,0,-1};
int diry[4]={1,0,-1,0};
int totalSpawn;
bool cakeBeTaken;
double prepow[35000];
对于游戏过程,幸运的是,这道题虽然是一个大模拟,但是出题人已经把整个进行过程写在了题面中了,这也使得我们很容易就能把主程序和输入输出程序给写完。对于这么良心的出题人,你们怎么好意思把它评成紫题呢?要像我一样选择入门难度!
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
inline void print()
{
if(nowt==t+1)
cout<<"The game is going on"<<endl;
else
cout<<"Game over after "<<nowt<<" seconds"<<endl;
list<antNode>::iterator it;
cout<<antCnt<<endl;
for(it=antList.begin();it!=antList.end();it++)
cout<<it->age<<" "<<it->level<<" "<<it->HP<<" "<<it->posx<<" "<<it->posy<<endl;
}
int main()
{
totalSpawn=0;
cakeBeTaken=false;
antCnt=0;
n=read(),m=read(),s=read(),d=read(),r=read();
for(int i=1;i<=s;i++)
{
tower[i].x=read();
tower[i].y=read();
mp[tower[i].x][tower[i].y].canMove=false;
}
t=read();
prepow[1]=1.1;
for(int i=2;i<=(t/6)+1;i++)
prepow[i]=prepow[i-1]*1.1;
for(nowt=1;nowt<=t;nowt++)
{
AntSpawn();
LeaveMessage();
Move();
GetCake();
TowerAttack();
DestoryBody();
if(GameOverCheck())
break;
LastUpdate();
}
print();
return 0;
}
考虑简单操作
分析整个游戏过程,我们发现,对于全部的操作而言,只有移动和攻击是十分麻烦的,对于其他的操作,由于我们使用了 容器,很容易就能维护。于是,我们先实现那些比较简单的需求。
对于蚂蚁生成,首先,我们需要考虑当前能否生成蚂蚁,如果蚂蚁已经到了上限,或者 $(0,0)$ 已经有了一只蚂蚁,就不生成,否则,生成一只新的蚂蚁。对于新蚂蚁的等级,我们可以通过总蚂蚁数量来计算得到,对于新蚂蚁的血量,由于我们已经预处理了$1.1 k$
,也可以在不使用 函数的情况下快速得到,于是,我们很容易就能写出生成蚂蚁的函数了。
inline void AntSpawn()
{
if(antCnt>=6||mp[0][0].canMove==false)
return;
antCnt++;
totalSpawn++;
int nowLevel=((totalSpawn-1)/6)+1;
antList.push_back(antNode(0,nowLevel,(int)(4.0*prepow[nowLevel]),0,0,false));
mp[0][0].canMove=false;
return;
}
对于信息素的生成,我们只需要从前往后遍历一遍我们的蚂蚁序列,对于背着蛋糕的蚂蚁,我们留下$5$信息素,反之我们留下2信息素。
inline void LeaveMessage()
{
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
{
if(it->isCake==true)
mp[it->posx][it->posy].message+=5;
else
mp[it->posx][it->posy].message+=2;
}
return;
}
对于蛋糕获取操作,我们首先遍历每一只蚂蚁,如果说当前蚂蚁在蛋糕上,且蛋糕没有被别的蚂蚁拿走,我们就让这只蚂蚁拿走蛋糕,更新蛋糕状态与蚂蚁血量。
inline void GetCake()
{
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
{
if(it->posx==n&&it->posy==m&&cakeBeTaken==false)
{
cakeBeTaken=true;
it->isCake=true;
it->HP=min(it->HP+(int)(floor)((double)it->maxHP/2.0),it->maxHP);
}
}
return;
}
对于清理尸体的操作,我们遍历每一只蚂蚁,如果当前蚂蚁的血量已经小于0了,我们就将这只蚂蚁给 $erase$ 掉,如果死亡的蚂蚁背着蛋糕,那我们重新更新蛋糕状态,反之则不更新。然后,我们更新蚂蚁的数量。在这里,我们需要注意到,如果对于当前的蚂蚁,我哦们将其删除,那我们的迭代器还是会指在那个删除的位置,当它自加之后,后面的蚂蚁也会往前进,这就导致我们会少遍历到那些本应该删除的蚂蚁,为了减少代码复杂度,我们定义一个局部变量来储存有没有蚂蚁被删除,如果有蚂蚁被删除了,那么在下一次遍历的时候,我们又从头开始遍历,由于蚂蚁数量的最大值是已知的,这个操作并不会太浪费时间。
inline void DestoryBody()
{
bool isErase=false;
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
{
if(isErase==true)
{
it=antList.begin();
isErase=false;
}
if(it->HP<0)
{
if(it->isCake)
{
it->isCake=false;
cakeBeTaken=false;
}
mp[it->posx][it->posy].canMove=true;
antList.erase(it);
antCnt--;
isErase=true;
}
}
return;
}
对于判断游戏是否失败,我们同样只需要遍历每一只蚂蚁,如果说这只蚂蚁存活,背着蛋糕,且在(0,0)这个位置上,那就终止游戏。
inline bool GameOverCheck()
{
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
if(it->isCake==true&&it->HP>=0&&it->posx==0&&it->posy==0)
return true;
return false;
}
对于每一秒结束,我们都需要减少地图上每个点的信息素,并增加当前存活的蚂蚁年龄。
inline void LastUpdate()
{
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
if(mp[i][j].message)
mp[i][j].message--;
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
it->age++;
return;
}
考虑移动
首先,我们阅读题目,关于移动的描述非常长而复杂,在提取有用信息后,我们发现,移动无非是这么一个过程。
从向东开始顺时针遍历每一个方向,如果只有一个信息素最大的点,我们朝着信息素最大的点前进,如果有多个,我们往最先遍历到的点前进。
如果没有一个点可以前往,我们把蚂蚁上一秒的位置更新到现在的位置,这意味着到了下一秒,蚂蚁就可以原路返回了。
如果蚂蚁的年龄之后是5的倍数,我们需要让这只蚂蚁逆时针找到第一个可以移动的方向作为它的前进方向。
更新地图上点的状态,更新蚂蚁的位置。
由于我们使用的是容器,我们只需要用迭代器访问容器内元素的地址就可以实现更改了,代码如下。
inline void Move()
{
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
{
int pos;
int ccMaxMessage=0;
int chooseCount=0;
for(int i=0;i<4;i++)
{
int ccx=it->posx+dirx[i],ccy=it->posy+diry[i];
if(mp[ccx][ccy].canMove==true&&(ccx!=it->prex||ccy!=it->prey)&&ccx>=0&&ccx<=n&&ccy>=0&&ccy<=m)
{
if(mp[ccx][ccy].message==ccMaxMessage)
{
pos=i;
chooseCount++;
}
else if(mp[ccx][ccy].message>ccMaxMessage)
{
ccMaxMessage=mp[ccx][ccy].message;
pos=i;
chooseCount=1;
}
}
}
if(chooseCount==0)
{
it->prex=it->posx;
it->prey=it->posy;
continue;
}
else if(chooseCount>1)
{
for(pos=0;pos<4;pos++)
{
int ccx=it->posx+dirx[pos],ccy=it->posy+diry[pos];
if(mp[ccx][ccy].canMove==true&&(ccx!=it->prex||ccy!=it->prey)&&ccx>=0&&ccx<=n&&ccy>=0&&ccy<=m&&mp[ccx][ccy].message==ccMaxMessage)
break;
}
}
if((it->age+1)%5==0)
{
for(int i=0;i<4;i++)
{
pos--;
if(pos<0)
pos+=4;
int ccx=it->posx+dirx[pos],ccy=it->posy+diry[pos];
if(mp[ccx][ccy].canMove==true&&(ccx!=it->prex||ccy!=it->prey)&&ccx>=0&&ccx<=n&&ccy>=0&&ccy<=m)
break;
}
}
it->prex=it->posx;
it->prey=it->posy;
it->posx+=dirx[pos];
it->posy+=diry[pos];
mp[it->prex][it->prey].canMove=true;
mp[it->posx][it->posy].canMove=false;
}
return;
}
考虑攻击
首先,我们仔细地阅读题目,发现攻击有以下几个步骤。
对于每一个防御塔的攻击目标,我们遍历每一只蚂蚁,如果有蚂蚁在其攻击范围内,我们优先选择攻击携带蛋糕的蚂蚁,其次选择距离最近的蚂蚁。
每一个防御塔开始攻击,
如果防御塔的目标是距离最近的蚂蚁,我们只需要扣除目标相应的血量,反之,我们运用向量的相关知识,判断攻击范围中的蚂蚁是否与激光有公共点,有就扣血。
在写完几何模板之后,我们很容易就能写出攻击的代码了。
struct Point
{
double x;
double y;
};
Point inline TurnPoint(double x,double y)
{
Point NewPoint;
NewPoint.x=x;
NewPoint.y=y;
return NewPoint;
}
Point operator +(Point A,Point B)
{
Point NewPoint=TurnPoint(A.x+B.x,A.y+B.y);
return NewPoint;
}
Point operator -(Point A,Point B)
{
Point NewPoint=TurnPoint(A.x-B.x,A.y-B.y);
return NewPoint;
}
inline int DoublePositive(double x)
{
if(fabs(x)<eps)
return 0;
if(x<0)
return -1;
return 1;
}
inline double Dot(Point A,Point B)
{
double sum=A.x*B.x+A.y*B.y;
return sum;
}
inline double Length(Point A)
{
double sum=sqrt(Dot(A,A));
return sum;
}
inline double Cross(Point A,Point B)
{
double sum=A.x*B.y-A.y*B.x;
return sum;
}
inline double GetSlope(Point A)
{
double sum=A.y/A.x;
return sum;
}
inline double GetPointDistantToSegment(Point P,Point A,Point B)
{
Point Vector_1=B-A;
Point Vector_2=P-A;
Point Vector_3=P-B;
if(DoublePositive(Dot(Vector_1,Vector_2))<0)
return Length(Vector_2);
if(DoublePositive(Dot(Vector_1,Vector_3))>0)
return Length(Vector_3);
return fabs(Cross(Vector_1,Vector_2)/Length(Vector_1));
}
inline double GetDistance(double tx,double ty,double ax,double ay)
{
return sqrt((tx-ax)*(tx-ax)+(ty-ay)*(ty-ay));
}
inline void TowerAttack()
{
list<antNode>::iterator it;
for(int i=1;i<=s;i++)
{
tower[i].haveTarget=false;
tower[i].minDis=99999999;
tower[i].haveCakeTarget=false;
for(it=antList.begin();it!=antList.end();it++)
{
double dis=GetDistance(tower[i].x,tower[i].y,it->posx,it->posy);
if(dis-r<=eps)
{
tower[i].haveTarget=true;
if(it->isCake)
{
tower[i].haveCakeTarget=true;
tower[i].target=it;
}
else if(dis<tower[i].minDis&&tower[i].haveCakeTarget==false)
{
tower[i].target=it;
tower[i].minDis=dis;
}
}
if(tower[i].haveCakeTarget)
break;
}
}
for(int i=1;i<=s;i++)
{
if(tower[i].haveTarget==false)
continue;
if(tower[i].haveCakeTarget==false)
tower[i].target->HP-=d;
else
{
Point A=TurnPoint(tower[i].target->posx,tower[i].target->posy);
Point B=TurnPoint(tower[i].x,tower[i].y);
for(it=antList.begin();it!=antList.end();it++)
{
if(it->isCake)
{
it->HP-=d;
continue;
}
Point P=TurnPoint(it->posx,it->posy);
double QAQDistance=GetPointDistantToSegment(P,A,B);
if(QAQDistance-0.5<eps)
it->HP-=d;
}
}
}
return;
}
完整代码
#include <bits/stdc++.h>
using namespace std;
const double eps=1e-10;
const int MAXN=10;
int n,m,s,d,t;
double r;
int antCnt;
int nowt;
int dirx[4]={0,1,0,-1};
int diry[4]={1,0,-1,0};
int totalSpawn;
bool cakeBeTaken;
double prepow[35000];
struct mpNode
{
int message=0;
bool canMove=true;
};
struct antNode
{
int age;
int level;
int HP;
int posx;
int posy;
int prex;
int prey;
int maxHP;
bool isCake;
antNode(int age,int level,int HP,int x,int y,bool isCake=false):
age(age),level(level),HP(HP),posx(x),posy(y),isCake(isCake),prex(x),prey(y),maxHP(HP){}
};
struct towerNode
{
int x;
int y;
double minDis;
bool haveTarget;
bool haveCakeTarget;
list<antNode>::iterator target;
};
mpNode mp[MAXN][MAXN];
list<antNode> antList;
towerNode tower[22];
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
struct Point
{
double x;
double y;
};
Point inline TurnPoint(double x,double y)
{
Point NewPoint;
NewPoint.x=x;
NewPoint.y=y;
return NewPoint;
}
Point operator +(Point A,Point B)
{
Point NewPoint=TurnPoint(A.x+B.x,A.y+B.y);
return NewPoint;
}
Point operator -(Point A,Point B)
{
Point NewPoint=TurnPoint(A.x-B.x,A.y-B.y);
return NewPoint;
}
inline int DoublePositive(double x)
{
if(fabs(x)<eps)
return 0;
if(x<0)
return -1;
return 1;
}
inline double Dot(Point A,Point B)
{
double sum=A.x*B.x+A.y*B.y;
return sum;
}
inline double Length(Point A)
{
double sum=sqrt(Dot(A,A));
return sum;
}
inline double Cross(Point A,Point B)
{
double sum=A.x*B.y-A.y*B.x;
return sum;
}
inline double GetSlope(Point A)
{
double sum=A.y/A.x;
return sum;
}
inline double GetPointDistantToSegment(Point P,Point A,Point B)
{
Point Vector_1=B-A;
Point Vector_2=P-A;
Point Vector_3=P-B;
if(DoublePositive(Dot(Vector_1,Vector_2))<0)
return Length(Vector_2);
if(DoublePositive(Dot(Vector_1,Vector_3))>0)
return Length(Vector_3);
return fabs(Cross(Vector_1,Vector_2)/Length(Vector_1));
}
inline double GetDistance(double tx,double ty,double ax,double ay)
{
return sqrt((tx-ax)*(tx-ax)+(ty-ay)*(ty-ay));
}
inline void AntSpawn()
{
if(antCnt>=6||mp[0][0].canMove==false)
return;
antCnt++;
totalSpawn++;
int nowLevel=((totalSpawn-1)/6)+1;
antList.push_back(antNode(0,nowLevel,(int)(4.0*prepow[nowLevel]),0,0,false));
mp[0][0].canMove=false;
return;
}
inline void LeaveMessage()
{
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
{
if(it->isCake==true)
mp[it->posx][it->posy].message+=5;
else
mp[it->posx][it->posy].message+=2;
}
return;
}
inline void Move()
{
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
{
int pos;
int ccMaxMessage=0;
int chooseCount=0;
for(int i=0;i<4;i++)
{
int ccx=it->posx+dirx[i],ccy=it->posy+diry[i];
if(mp[ccx][ccy].canMove==true&&(ccx!=it->prex||ccy!=it->prey)&&ccx>=0&&ccx<=n&&ccy>=0&&ccy<=m)
{
if(mp[ccx][ccy].message==ccMaxMessage)
{
pos=i;
chooseCount++;
}
else if(mp[ccx][ccy].message>ccMaxMessage)
{
ccMaxMessage=mp[ccx][ccy].message;
pos=i;
chooseCount=1;
}
}
}
if(chooseCount==0)
{
it->prex=it->posx;
it->prey=it->posy;
continue;
}
else if(chooseCount>1)
{
for(pos=0;pos<4;pos++)
{
int ccx=it->posx+dirx[pos],ccy=it->posy+diry[pos];
if(mp[ccx][ccy].canMove==true&&(ccx!=it->prex||ccy!=it->prey)&&ccx>=0&&ccx<=n&&ccy>=0&&ccy<=m&&mp[ccx][ccy].message==ccMaxMessage)
break;
}
}
if((it->age+1)%5==0)
{
for(int i=0;i<4;i++)
{
pos--;
if(pos<0)
pos+=4;
int ccx=it->posx+dirx[pos],ccy=it->posy+diry[pos];
if(mp[ccx][ccy].canMove==true&&(ccx!=it->prex||ccy!=it->prey)&&ccx>=0&&ccx<=n&&ccy>=0&&ccy<=m)
break;
}
}
it->prex=it->posx;
it->prey=it->posy;
it->posx+=dirx[pos];
it->posy+=diry[pos];
mp[it->prex][it->prey].canMove=true;
mp[it->posx][it->posy].canMove=false;
}
return;
}
inline void GetCake()
{
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
{
if(it->posx==n&&it->posy==m&&cakeBeTaken==false)
{
cakeBeTaken=true;
it->isCake=true;
it->HP=min(it->HP+(int)(floor)((double)it->maxHP/2.0),it->maxHP);
}
}
return;
}
inline void TowerAttack()
{
list<antNode>::iterator it;
for(int i=1;i<=s;i++)
{
tower[i].haveTarget=false;
tower[i].minDis=99999999;
tower[i].haveCakeTarget=false;
for(it=antList.begin();it!=antList.end();it++)
{
double dis=GetDistance(tower[i].x,tower[i].y,it->posx,it->posy);
if(dis-r<=eps)
{
tower[i].haveTarget=true;
if(it->isCake)
{
tower[i].haveCakeTarget=true;
tower[i].target=it;
}
else if(dis<tower[i].minDis&&tower[i].haveCakeTarget==false)
{
tower[i].target=it;
tower[i].minDis=dis;
}
}
if(tower[i].haveCakeTarget)
break;
}
}
for(int i=1;i<=s;i++)
{
if(tower[i].haveTarget==false)
continue;
if(tower[i].haveCakeTarget==false)
tower[i].target->HP-=d;
else
{
Point A=TurnPoint(tower[i].target->posx,tower[i].target->posy);
Point B=TurnPoint(tower[i].x,tower[i].y);
for(it=antList.begin();it!=antList.end();it++)
{
if(it->isCake)
{
it->HP-=d;
continue;
}
Point P=TurnPoint(it->posx,it->posy);
double QAQDistance=GetPointDistantToSegment(P,A,B);
if(QAQDistance-0.5<eps)
it->HP-=d;
}
}
}
return;
}
inline void DestoryBody()
{
bool isErase=false;
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
{
if(isErase==true)
{
it=antList.begin();
isErase=false;
}
if(it->HP<0)
{
if(it->isCake)
{
it->isCake=false;
cakeBeTaken=false;
}
mp[it->posx][it->posy].canMove=true;
antList.erase(it);
antCnt--;
isErase=true;
}
}
return;
}
inline bool GameOverCheck()
{
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
if(it->isCake==true&&it->HP>=0&&it->posx==0&&it->posy==0)
return true;
return false;
}
inline void LastUpdate()
{
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
if(mp[i][j].message)
mp[i][j].message--;
list<antNode>::iterator it;
for(it=antList.begin();it!=antList.end();it++)
it->age++;
return;
}
inline void print()
{
if(nowt==t+1)
cout<<"The game is going on"<<endl;
else
cout<<"Game over after "<<nowt<<" seconds"<<endl;
list<antNode>::iterator it;
cout<<antCnt<<endl;
for(it=antList.begin();it!=antList.end();it++)
cout<<it->age<<" "<<it->level<<" "<<it->HP<<" "<<it->posx<<" "<<it->posy<<endl;
}
int main()
{
totalSpawn=0;
cakeBeTaken=false;
antCnt=0;
n=read(),m=read(),s=read(),d=read(),r=read();
for(int i=1;i<=s;i++)
{
tower[i].x=read();
tower[i].y=read();
mp[tower[i].x][tower[i].y].canMove=false;
}
t=read();
prepow[1]=1.1;
for(int i=2;i<=(t/6)+1;i++)
prepow[i]=prepow[i-1]*1.1;
for(nowt=1;nowt<=t;nowt++)
{
AntSpawn();
LeaveMessage();
Move();
GetCake();
TowerAttack();
DestoryBody();
if(GameOverCheck())
break;
LastUpdate();
}
print();
return 0;
}
由洛谷搬运而来,请见谅!!
这里空空如也






有帮助,赞一个