您的位置:首页 > 其它

[BZOJ1033][ZJOI2008]杀蚂蚁antbuster(大模拟)

2017-02-20 14:21 405 查看

题目描述

传送门

题解

bz的题面真心不爽,建议去codevs

比较良心的一道大模拟,题面写的比较清楚,也没有什么坑

几个需要注意的地方

1、对于每一只蚂蚁来说,年龄=秒数-1

2、选择方向的过程是:首先根据规则1-3选出一个方向,这个时候判断如果秒数不是5的倍数的话就直接走过去;如果是5的倍数就按照下一个规则继续选一个方向然后走过去。注意可达点的定义以及各种前提(先可达、再信息素最大)

3、杀死一只蚂蚁之后,需要更新:地图,当前蚂蚁数量,蛋糕

4、炮攻击的时候用计算几何的方法算点线距

剩下的严格根据题目描述和顺序就行了

具体看代码

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 200005

double eps=1e-9;
int dcmp(double x)
{
if (x<=eps&&x>=-eps) return 0;
return (x>0)?1:-1;
}
struct Vector
{
double x,y;
Vector(double X=0,double Y=0)
{
x=X,y=Y;
}
};
typedef Vector Point;
Vector operator + (Vector a,Vector b) {return Vector(a.x+b.x,a.y+b.y);}
Vector operator - (Vector a,Vector b) {return Vector(a.x-b.x,a.y-b.y);}
bool operator == (Vector a,Vector b) {return a.x==b.x&&a.y==b.y;}

double Dot(Vector a,Vector b)
{
return a.x*b.x+a.y*b.y;
}
double Cross(Vector a,Vector b)
{
return a.x*b.y-a.y*b.x;
}
double Len(Vector a)
{
return sqrt(a.x*a.x+a.y*a.y);
}
double DisTS(Point P,Point A,Point B)
{
if (A==B) return Len(P-A);
Vector v=B-A,w=P-A,u=P-B;
if (dcmp(Dot(v,w))<0) return Len(w);
else if (dcmp(Dot(v,u))>0) return Len(u);
else return fabs(Cross(v,w)/Len(v));
}

//n,m是地图范围,guncnt为炮的个数,d为每一次攻击的血量,r为攻击半径,t为模拟时间,T为当前时间
int n,m,guncnt,d,r,t,T;
//蚂蚁
struct ANT
{
//当前位置,上一次的位置
int x,y,lastx,lasty;
//年龄(秒数)
int age;
//等级
int level;
//起始血量,实际血量
int startblood,blood;
//是否扛着蛋糕
bool carry;
}ant[10];
//炮
struct GUN
{
//位置
int x,y;
}gun[25];
//antcnt当前蚂蚁的总数,tot所有蚂蚁的总数,cake为扛着蛋糕的蚂蚁编号
int antcnt,tot,cake;
//mp是地图,为0表示什么都没有,为-1表示有炮,为正数表示有几只蚂蚁
int mp[10][10];
//infor为信息素的值
int infor[10][10];
//为1.1的n次方,用于算起始血量
double mi
;
//向上、左、下、右走
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
//判断游戏是否提前结束
bool flag;

void AntBirth()
{
if (antcnt<6&&!mp[0][0])
{
++antcnt;++tot;
ant[antcnt].x=ant[antcnt].y=ant[antcnt].lastx=ant[antcnt].lasty=0;
ant[antcnt].age=1;
ant[antcnt].level=(tot-1)/6+1;
ant[antcnt].startblood=ant[antcnt].blood=floor(4*mi[ant[antcnt].level]);
ant[antcnt].carry=0;
++mp[0][0];
}
}
void Information()
{
for (int i=1;i<=antcnt;++i)
{
if (ant[i].carry) infor[ant[i].x][ant[i].y]+=5;
else infor[ant[i].x][ant[i].y]+=2;
}
}
void Move()
{
// 0 上 2 下 1 左 3 右
for (int i=1;i<=antcnt;++i)
{
//按照1-3的规则先选定一个方向

// x,y为这只蚂蚁当前的点,lastx,lasty为上一次来的点
int x=ant[i].x,y=ant[i].y;
int lastx=ant[i].lastx,lasty=ant[i].lasty;
// can[i]表示4个方向,向这个方向走是不是可达点
bool can[4];memset(can,0,sizeof(can));

//nxtx,nxty为下一步到达的点
//Max为可达点中信息素的最大值
//cancnt为可达点个数
int Max=0,cancnt=0;
for (int j=0;j<4;++j)
{
int nxtx=x+dx[j],nxty=y+dy[j];
if (nxtx>=0&&nxtx<=n&&nxty>=0&&nxty<=m&&mp[nxtx][nxty]==0&&(nxtx!=lastx||nxty!=lasty))
{
can[j]=1;++cancnt;
Max=max(Max,infor[nxtx][nxty]);
}
}
if (!cancnt)
{
ant[i].lastx=x;ant[i].lasty=y;
continue;
}
int dir=0;
for (int j=3;j>=0;--j)
{
int nxtx=x+dx[j],nxty=y+dy[j];
if (can[j]&&infor[nxtx][nxty]==Max)
{
dir=j;
if (ant[i].age%5==0) break;
--mp[x][y];++mp[nxtx][nxty];
ant[i].lastx=x,ant[i].lasty=y;
ant[i].x=nxtx,ant[i].y=nxty;
break;
}
}

//如果满足年龄是5的倍数,顺时针旋转,找到一个可达点
if (ant[i].age%5==0)
{
for (int j=0;j<4;++j)
{
++dir;if (dir==4) dir=0;
if (can[dir])
{
int nxtx=x+dx[dir],nxty=y+dy[dir];
--mp[x][y];++mp[nxtx][nxty];
ant[i].lastx=x,ant[i].lasty=y;
ant[i].x=nxtx,ant[i].y=nxty;
break;
}
}
}
}
}
void CarryCake()
{
if (cake) return;
for (int i=1;i<=antcnt;++i)
if (ant[i].x==n&&ant[i].y==m)
{
ant[i].carry=1;cake=i;
//增加血量
ant[i].blood+=ant[i].startblood/2;
ant[i].blood=min(ant[i].blood,ant[i].startblood);
break;
}
}
int qr(int x)
{
return x*x;
}
int length(ANT a,GUN b)
{
return qr(a.x-b.x)+qr(a.y-b.y);
}
bool canhit(ANT a,GUN b,ANT c)
{
Point A=Point(b.x,b.y);
Point B=Point(c.x,c.y);
Point P=Point(a.x,a.y);
double dis=DisTS(P,A,B);
if (dcmp(dis-0.5)<=0) return 1;
else return 0;
}
void Attack()
{
//length(ant,gun)表示这个蚂蚁和这个炮距离的平方
for (int i=1;i<=guncnt;++i)
{
int goal=0;
if (cake&&length(ant[cake],gun[i])<=r*r) goal=cake;
else
{
int Min=100000;
for (int j=1;j<=antcnt;++j)
{
int now=length(ant[j],gun[i]);
if (now<=r*r&&now<Min)
{
Min=now;
goal=j;
}
}
}
if (!goal) continue;
//canhit(i,j,k)表示i这只蚂蚁是否在j这个炮和k这个蚂蚁的连线上
for (int j=1;j<=antcnt;++j)
if (canhit(ant[j],gun[i],ant[goal])) ant[j].blood-=d;
}
}
void CheckDeath()
{
int i=1;
while (i<=antcnt)
{
if (ant[i].blood<0)
{
--mp[ant[i].x][ant[i].y];
for (int j=i;j<antcnt;++j)
ant[j]=ant[j+1];
--antcnt;
}
else ++i;
}
cake=0;
for (int i=1;i<=antcnt;++i)
if (ant[i].carry) {cake=i;break;}
}
bool CheckGame()
{
if (!cake) return 0;
for (int i=1;i<=antcnt;++i)
if (!ant[i].x&&!ant[i].y&&ant[i].carry) return 1;
return 0;
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%d%d%d",&guncnt,&d,&r);
for (int i=1;i<=guncnt;++i)
{
scanf("%d%d",&gun[i].x,&gun[i].y);
mp[gun[i].x][gun[i].y]=-1;
}
scanf("%d",&t);
mi[0]=1.0;for (int i=1;i<=t;++i) mi[i]=mi[i-1]*1.1;

for (T=1;T<=t;++T)
{
//start the second

// 出生一只蚂蚁
AntBirth();

// 蚂蚁留下信息素
Information();

// 将蚂蚁移动
Move();

// 蚂蚁要扛蛋糕
CarryCake();

// 炮开始攻击
Attack();

// 检查蚂蚁的死亡情况以及蛋糕是否归位
CheckDeath();

// 判断游戏是否结束
flag=CheckGame();
if (flag) break;

for (int i=0;i<=n;++i)
for (int j=0;j<=m;++j)
if (infor[i][j]) --infor[i][j];
for (int i=1;i<=antcnt;++i)
++ant[i].age;
//end the second
}
if (flag) printf("Game over after %d seconds\n",T);
else puts("The game is going on");
printf("%d\n",antcnt);
for (int i=1;i<=antcnt;++i)
printf("%d %d %d %d %d\n",ant[i].age-1,ant[i].level,ant[i].blood,ant[i].x,ant[i].y);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: