「BZOJ1033」[ZJOI2008] 杀蚂蚁antbuster
Description
最近,佳佳迷上了一款好玩的小游戏:antbuster。游戏规则非常简单:在一张地图上,左上角是蚂蚁窝,右下角是蛋糕,蚂蚁会源源不断地从窝里爬出来,试图把蛋糕搬回蚂蚁窝。而你的任务,就是用原始资金以及杀蚂蚁获得的奖金造防御塔,杀掉这些试图跟你抢蛋糕的蚂蚁~ 下附一张游戏截图:
为了拿到尽可能高的分数,佳佳设计了很多种造塔的方案,但在尝试了其中的一小部分后,佳佳发现,这个游戏实在是太费时间了。为了节省时间,佳佳决定写个程序,对于每一种方案,模拟游戏进程,根据效果来判断方案的优劣。根据自己在游戏中积累的一些经验,以及上网搜到的一些参数,佳佳猜了蚂蚁爬行的算法,并且假设游戏中的蚂蚁也是按这个规则选择路线: 1、每一秒钟开始的时候,蚂蚁都在平面中的某个整点上。如果蚂蚁没有扛着蛋糕,它会在该点留下2单位的信息素,否则它会留下5单位的信息素。然后蚂蚁会在正北、正南、正东、正西四个方向中选择一个爬过去。 2、选择方向的规则是:首先,爬完一个单位长度后到达的那个点上,不能有其他蚂蚁或是防御塔,并且那个点不能是蚂蚁上一秒所在的点(除非上一个时刻蚂蚁就被卡住,且这个时刻它仍无法动),当然,蚂蚁也不会爬出地图的边界(我们定义这些点为不可达点)。如果此时有多个选择,蚂蚁会选择信息素最多的那个点爬过去。 3、如果此时仍有多种选择,蚂蚁先面向正东,如果正东不是可选择的某个方向,它会顺时针转90°,再次判断,如果还不是,再转90°…直到找到可以去的方向。 4、如果将每只蚂蚁在洞口出现的时间作为它的活动时间的第1秒,那么每当这只蚂蚁的活动时间秒数为5的倍数的时候,它先按规则1~3确定一个方向,面对该方向后逆时针转90°,若它沿当前方向会走到一个不可达点,它会不停地每次逆时针转90°,直到它面对着一个可达的点,这样定下的方向才是蚂蚁最终要爬去的方向。 5、如果蚂蚁的四周都是不可达点,那么蚂蚁在这一秒内会选择停留在当前点。下一秒判断移动方向时,它上一秒所在点为其当前停留的点。 6、你可以认为蚂蚁在选定方向后,瞬间移动到它的目标点,这一秒钟剩下的时间里,它就停留在目标点。 7、蚂蚁按出生的顺序移动,出生得比较早的蚂蚁先移动。 然后,是一些有关地图的信息: 1、 每一秒,地图所有点上的信息素会损失1单位,如果那个点上有信息素的话。 2、 地图上某些地方是炮台。炮台的坐标在输入中给出。 3、 地图的长、宽在输入中给出,对于n * m的地图,它的左上角坐标为(0,0),右下角坐标为(n,m)。蚂蚁洞的位置为(0,0),蛋糕的位置为(n,m)。 4、 你可以把蚂蚁看做一个直径为1单位的圆,圆心位于蚂蚁所在的整点。 5、 游戏开始时,地图上没有蚂蚁,每个点上的信息素含量均为0。 一些有关炮塔的信息: 1、 炮塔被放置在地图上的整点处。 2、 为了简单一些,我们认为这些炮塔都是激光塔。激光塔的射速是1秒/次,它的攻击伤害为d/次,攻击范围为r。你可以认为每秒蚂蚁移动完毕后,塔才开始攻击。并且,只有当代表蚂蚁的圆的圆心与塔的直线距离不超过r时,塔才算打得到那只蚂蚁。 3、 如果一只蚂蚁扛着蛋糕,那么它会成为target,也就是说,任何打得到它的塔的炮口都会对准它。如果蛋糕好好地呆在原位,那么每个塔都会挑离它最近的蚂蚁进行攻击,如果有多只蚂蚁,它会选出生较早的一只。 4、 激光塔有个比较奇怪的特性:它在选定了打击目标后,只要目标在其射程内,塔到目标蚂蚁圆心的连线上的所有蚂蚁(这里“被打到”的判定变成了表示激光的线段与表示蚂蚁的圆有公共点)都会被打到并损d格血,但激光不会穿透它的打击目标打到后面的蚂蚁。 5、 尽管在真实游戏中,塔是可以升级的,但在这里我们认为塔的布局和等级就此定了下来,不再变动。 再介绍一下蚂蚁窝: 1、 如果地图上的蚂蚁不足6只,并且洞口没有蚂蚁,那么窝中每秒会爬出一只蚂蚁,直到地图上的蚂蚁数为6只。 2、 刚出生的蚂蚁站在洞口。 3、 每只蚂蚁有一个级别,级别决定了蚂蚁的血量,级别为k的蚂蚁的血量为int(4*1.1^k)(int(x)表示对x取下整)。每被塔打一次,蚂蚁的血减少d。注意,血量为0的蚂蚁仍能精力充沛地四处乱爬,只有一只蚂蚁的血被打成负数时,它才算挂了。 4、 蚂蚁的级别是这样算的:前6只出生的蚂蚁是1级,第7~12只是2级,依此类推。 最后给出关于蛋糕的介绍: 1、 简单起见,你可以认为此时只剩最后一块蛋糕了。如果有蚂蚁走到蛋糕的位置,并且此时蛋糕没有被扛走,那么这只蚂蚁就扛上了蛋糕。蚂蚁被打死后蛋糕归位。 2、 如果一只扛着蛋糕的蚂蚁走到蚂蚁窝的位置,我们就认为蚂蚁成功抢到了蛋糕,游戏结束。 3、 蚂蚁扛上蛋糕时,血量会增加int(该蚂蚁出生时血量 / 2),但不会超过上限。 整理一下1秒钟内发生的事件: 1秒的最初,如果地图上蚂蚁数不足6,一只蚂蚁就会在洞口出生。接着,蚂蚁们在自己所在点留下一些信息素后,考虑移动。先出生的蚂蚁先移动。移动完毕后,如果有蚂蚁在蛋糕的位置上并且蛋糕没被拿走,它把蛋糕扛上,血量增加,并在这时被所有塔设成target。然后所有塔同时开始攻击。如果攻击结束后那只扛着蛋糕的蚂蚁挂了,蛋糕瞬间归位。攻击结束后,如果发现扛蛋糕的蚂蚁没死并在窝的位置,就认为蚂蚁抢到了蛋糕。游戏也在此时结束。最后,地图上所有点的信息素损失1单位。所有蚂蚁的年龄加1。漫长的1秒到此结束。
Input
输入的第一行是2个用空格隔开的整数,n、m,分别表示了地图的长和宽。第二行是3个用空格隔开的整数,s、d、r,依次表示炮塔的个数、单次攻击伤害以及攻击范围。接下来s行,每行是2个用空格隔开的整数x、y,描述了一个炮塔的位置。当然,蚂蚁窝的洞口以及蛋糕所在的位置上一定没有炮塔。最后一行是一个正整数t,表示我们模拟游戏的前t秒钟。
Output
如果在第t秒或之前蚂蚁抢到了蛋糕,输出一行“Game over after x seconds”,其中x为游戏结束的时间,否则输出“The game is going on”。如果游戏在t秒或之前结束,输出游戏结束时所有蚂蚁的信息,否则输出t秒后所有蚂蚁的信息。格式如下:第一行是1个整数s,表示此时活着的蚂蚁的总数。接下来s行,每行5个整数,依次表示一只蚂蚁的年龄(单位为秒)、等级、当前血量,以及在地图上的位置(a,b)。输出按蚂蚁的年龄递减排序。
Sample Input
2 10 1
7 8
8 6
5
Sample Output
5
5 1 4 1 4
4 1 4 0 4
3 1 4 0 3
2 1 4 0 2
1 1 4 0 1
HINT
样例说明:
3*5的地图,有1个单次伤害为1、攻击范围为2的激光炮塔,它的位置为(2,2),模拟游戏的前5秒。5秒内有5只蚂蚁出生,都是向东爬行,其中第1~4只在路过(0,2)点时被激光塔伤了1格血。在第5秒的时候,最早出生的蚂蚁按移动规则1~3本来该向东移动,但由于规则4的作用,它在发现向北和向西移动都会到达不可达点后,最终选择了向南移动。
数据说明:
100%的数据满足1 ≤ n,m ≤ 8,s ≤ 20,t ≤ 200,000
题解
由于开了O2优化,在bzoj交此题应该尽量避免使用double
否则可能出现本机通过但是bzoj过不了
lydsy的管理员回复邮件还是很快的23333,原来的样例没有第一行TAT
写这个真是作死。。
lwh:一脚踩下去死了一半蚂蚁,后来发现剩下的蚂蚁要要一只只用手抓,后来发现看不见蚂蚁了(这就是做这题的过程)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int n,m,s,tur_d,r,T,born; int in[11][11]; int xx[4]={0,1,0,-1},yy[4]={1,0,-1,0}; bool getcake,mp[11][11]; struct tur{int x,y;}tur[31]; struct ant{int lev,hp,age,x,y,lx,ly,mx;bool live,cake;}a[11]; struct point{int x,y;}; struct line{point a,b;}l; point sub(point a,point b){point t;t.x=a.x-b.x;t.y=a.y-b.y;return t;} int cmul(point a,point b){return a.x*b.y-a.y*b.x;} int turn(point a,point b,point c){return cmul(sub(b,a),sub(c,a));} int sqr(int x){return x*x;} double caldis(int x1,int y1,int x2,int y2) {return sqrt(sqr(x1-x2)+sqr(y1-y2));} int cdis(int x,int y) {return sqr(tur[x].x-a[y].x)+sqr(tur[x].y-a[y].y);} double getdis(int x,int y) {return sqrt(cdis(x,y));} bool cmp(ant a,ant b){return a.age>b.age;} void bornint(int k) { int l=born/6+1; a[k].lev=l; a[k].hp=a[k].mx=int(4*pow(1.1,l)); a[k].age=0;a[k].live=1; a[k].x=a[k].y=a[k].lx=a[k].ly=0; mp[0][0]=1; born++; } bool jud(int x,int y,int lx,int ly) { if(mp[x][y]||x<0||y<0||x>n||y>m)return 0; if(x==lx&&y==ly)return 0; return 1; } void move(int k,int dir) { int x=a[k].x,y=a[k].y; if(dir==-1){a[k].lx=x;a[k].ly=y;return;} int nowx=x+xx[dir],nowy=y+yy[dir]; mp[x][y]=0;mp[nowx][nowy]=1; a[k].lx=x;a[k].ly=y;a[k].x=nowx;a[k].y=nowy; } void spmove(int k,int dir) { int x=a[k].x,y=a[k].y,lx=a[k].lx,ly=a[k].ly; for(int i=(dir-1+4)%4;;i=(i-1+4)%4) { int nowx=x+xx[i],nowy=y+yy[i]; if(jud(nowx,nowy,lx,ly)) {move(k,i);return;} } } void premove(int k) { int x=a[k].x,y=a[k].y,lx=a[k].lx,ly=a[k].ly; int mx=-0x7fffffff,dir=-1; for(int i=0;i<4;i++) { int nowx=x+xx[i],nowy=y+yy[i]; if(jud(nowx,nowy,lx,ly)&&mx<in[nowx][nowy]) mx=in[nowx][nowy]; } for(int i=0;i<4;i++) { int nowx=x+xx[i],nowy=y+yy[i]; if(jud(nowx,nowy,lx,ly)&&(mx==in[nowx][nowy])){dir=i;break;} } if((a[k].age+1)%5!=0||dir==-1)move(k,dir); else spmove(k,dir); } bool cross(int x,int y) { double d=caldis(l.a.x,l.a.y,l.b.x,l.b.y); if(x==l.a.x&&y==l.a.y||x==l.b.x&&y==l.b.y)return 1; int x1=min(l.a.x,l.b.x),x2=max(l.a.x,l.b.x); int y1=min(l.a.y,l.b.y),y2=max(l.a.y,l.b.y); if(x<x1||x>x2|y<y1||y>y2)return 0; point p;p.x=x;p.y=y; if(fabs(turn(l.a,l.b,p))/d<=0.5)return 1; return 0; } void attack(int k) { int tmp=-1,dis=0x7fffffff; for(int i=1;i<=6;i++)if(a[i].live) { int d=cdis(k,i); if(d<=r*r) { if(a[i].cake)tmp=i; else if(!a[tmp].cake&&d<dis) {dis=d;tmp=i;} } } if(tmp==-1)return; l.a.x=tur[k].x;l.a.y=tur[k].y; l.b.x=a[tmp].x;l.b.y=a[tmp].y; for(int i=1;i<=6;i++) if(a[i].live) { if(cross(a[i].x,a[i].y)) a[i].hp-=tur_d; } } bool solve(int t) { if(!mp[0][0]) for(int i=1;i<=6;i++) if(!a[i].live) {bornint(i);break;} sort(a+1,a+7,cmp); for(int i=1;i<=6;i++)if(a[i].live) { int x=a[i].x,y=a[i].y; if(a[i].cake)in[x][y]+=5; else in[x][y]+=2; } for(int i=1;i<=6;i++)if(a[i].live) premove(i); if(!getcake) for(int i=1;i<=6;i++)if(a[i].live) if(a[i].x==n&&a[i].y==m) { a[i].cake=1; getcake=1; a[i].hp=min(a[i].mx,a[i].hp+a[i].mx/2); } for(int i=1;i<=s;i++)attack(i); for(int i=1;i<=6;i++)if(a[i].live) if(a[i].hp<0) { mp[a[i].x][a[i].y]=0; a[i].live=0; if(a[i].cake)a[i].cake=getcake=0; } if(getcake) for(int i=1;i<=6;i++)if(a[i].live) if(a[i].x==0&&a[i].y==0&&a[i].cake)return 1; for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) if(in[i][j]>0)in[i][j]--; for(int i=1;i<=6;i++)if(a[i].live) a[i].age++; return 0; } void ini() { scanf("%d%d",&n,&m); scanf("%d%d%d",&s,&tur_d,&r); for(int i=1;i<=s;i++) { scanf("%d%d",&tur[i].x,&tur[i].y); mp[tur[i].x][tur[i].y]=1; } scanf("%d",&T); } void print() { int cot=0; sort(a+1,a+7,cmp); for(int i=1;i<=6;i++)if(a[i].live)cot++; printf("%d\n",cot); for(int i=1;i<=6;i++) if(a[i].live) printf("%d %d %d %d %d\n",a[i].age,a[i].lev,a[i].hp,a[i].x,a[i].y); } int main() { ini(); for(int i=1;i<=T;i++) if(solve(i)) { printf("Game over after %d seconds\n",i); print(); return 0; } printf("The game is going on\n"); print(); return 0; } |
弱弱的问一句穿透怎么判呀
早忘了
无限仰慕…我写了263行…然后提交就WA了..现在还在Debug..