[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); }
相关文章推荐
- BZOJ 1033: [ZJOI2008]杀蚂蚁antbuster(模拟)
- 【BZOJ1033】[ZJOI2008]杀蚂蚁antbuster【模拟】
- BZOJ 1033 [ZJOI2008]杀蚂蚁antbuster 模拟
- [BZOJ 1033] [ZJOI2008] 杀蚂蚁antbuster 【模拟!】
- 【BZOJ1033】[ZJOI2008]杀蚂蚁antbuster【模拟】
- 【BZOJ 1033】 [ZJOI2008]杀蚂蚁antbuster(判断线段是否和圆相交)
- [BZOJ 1033][ZJOI2008]杀蚂蚁antbuster
- bzoj千题计划121:bzoj1033: [ZJOI2008]杀蚂蚁antbuster
- bzoj1033: [ZJOI2008]杀蚂蚁antbuster
- 【BZOJ 1033】 [ZJOI2008]杀蚂蚁antbuster
- 【BZOJ 1033】 [ZJOI2008]杀蚂蚁antbuster
- bzoj1033: [ZJOI2008]杀蚂蚁antbuster
- bzoj1033 [ZJOI2008]杀蚂蚁antbuster
- BZOJ 1033: [ZJOI2008]杀蚂蚁antbuster
- 【BZOJ1033】[ZJOI2008]杀蚂蚁antbuster
- 模拟 [ZJOI2008]杀蚂蚁antbuster
- [bzoj1033] [ZJOI2008]杀蚂蚁 Big MoNI
- bzoj 1033【ZJOI2008】杀蚂蚁
- 【模拟】【计算几何】[ZJOI2008][HYSBZ/BZOJ1033]杀蚂蚁antbuster
- 【BZOJ1038】【ZJOI2008】瞭望塔 [模拟退火]