《杀蚂蚁》有感
昨天下午给别人调了一会儿杀蚂蚁(炸精度了),于是乎自己心血来潮也想写写这道神题。
写代码没费多久,主要都是调代码。调了三节课,时间这么短的原因是因为我是对着标程调的(直接把下午调过了的那份代码输出调试信息然后两边手动fc……)。感觉浪费了这么一道神题。
总的来说,杀蚂蚁技术含量不高,唯一有点技术含量的就是判断线段与圆相交(计算几何)。其他的,就是个大模拟。不过细节很多,而且很多都是很难注意到的。
判断线段与圆相交的时候我用的是特殊方法(只适用于本题)。
因为蚂蚁都在整点上,而直径都是1,因此只要看那些离炮塔距离更近的蚂蚁即可。又因为是线段而不是直线,所以只有和Target蚂蚁在炮塔的同一侧的蚂蚁才会被打到,因此我就用了点积判同侧,最后再用叉积算点到直线距离。这是特殊方法,对于一般情况还是不适用的。
这题细节真心很多,而且很多都让人猝不及防。比如:
1. 所有炮塔同时开炮,所以蚂蚁被打到负血之后还可以被打,蚂蚁的死亡必须在炮塔攻击之后统一进行。
2. 小心精度问题,选择最近的蚂蚁和线段与圆相交可能炸精度(虽然对于本题来说蚂蚁都在整点上线段与圆相交不太可能炸精度),我调别人的代码调了一个多小时就是因为前者炸精度了。垃圾O2优化。
3. 蚂蚁的移动是有先后的,先动的蚂蚁可能会挡住后动的蚂蚁。
4. 扛蛋糕不一定是在走了一步之后,有那种扛蛋糕的蚂蚁死了然后原地不动的蚂蚁扛上了蛋糕的事情(我就被这玩意儿坑了)。
5. 线段与圆相交要小心写,不要写错。
6. 蚂蚁的移动要小心,真的很麻烦。
总之,虽然说是练了练代码能力,但是感觉还没有二逼平衡树、Mokia价值高(Mokia一开始打死不过,后来重写了才A的)。都怪我对着标程调,罪过罪过。
最后贴个代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 using namespace std; 6 const int dx[]={0,1,0,-1},dy[]={1,0,-1,0}; 7 const double eps=1e-6; 8 struct ANTS{ 9 int age,life,level,bornlife,x,y,lastx,lasty; 10 bool havecake; 11 ANTS(){} 12 bool operator<(const ANTS &a)const{ 13 return age>a.age; 14 } 15 void getcake(){ 16 havecake=true; 17 life+=(bornlife>>1); 18 if(life>bornlife)life=bornlife; 19 } 20 }a[1000010]; 21 struct TOWER{int x,y;}b[100010]; 22 void bornant(); 23 void leavesth(); 24 void moveants(); 25 void attack(); 26 void judgeend(); 27 void endofround(); 28 void printwin(); 29 void printlose(); 30 void moveit(int); 31 bool can(int,int,int); 32 void moveto(int,int,int); 33 void trytoattack(int); 34 double dis(int,int); 35 double dis3(int,int,int); 36 void hitant(int); 37 void dieant(int); 38 void maintainants(); 39 int dcmp(double,double); 40 int n,m,k,d,r,in[1010][1010]; 41 bool ant[1010][1010],tow[1010][1010],lose; 42 int tim,T; 43 int cak; 44 int cnt=0,head=1,tail=1; 45 int main(){ 46 #define MINE 47 #ifdef MINE 48 freopen("antbuster_ex.in","r",stdin); 49 freopen("antbuster_ex.out","w",stdout); 50 #endif 51 scanf("%d%d%d%d%d",&n,&m,&k,&d,&r); 52 for(int i=1;i<=k;i++){ 53 scanf("%d%d",&b[i].x,&b[i].y); 54 tow[b[i].x][b[i].y]=true; 55 } 56 scanf("%d",&T); 57 for(tim=1;tim<=T;tim++){ 58 //printf("TIME:%d\n",tim); 59 bornant();//printf("1"); 60 leavesth();//printf("2"); 61 moveants();//printf("3"); 62 attack();//printf("4"); 63 judgeend();//printf("5"); 64 if(lose)break; 65 endofround();//printf("6\n"); 66 //printf("cnt=%d\n",cnt); 67 //printwin();printf("\n"); 68 } 69 if(lose)printlose(); 70 else printwin(); 71 #ifndef MINE 72 printf("\n-------------------------DONE-------------------------\n"); 73 for(;;); 74 #endif 75 return 0; 76 } 77 void bornant(){ 78 if(cnt<6&&!ant[0][0]){ 79 //printf("%d borns\n",tail); 80 cnt++; 81 a[tail].age=1; 82 a[tail].level=(tail-1)/6+1; 83 a[tail].life=a[tail].bornlife=(int)(4*pow(1.1,a[tail].level)); 84 a[tail].x=a[tail].y=0; 85 ant[0][0]=true; 86 a[tail].lastx=a[tail].lasty=-1; 87 tail++; 88 } 89 } 90 void leavesth(){ 91 for(int i=head;i!=tail;i++){ 92 if(a[i].havecake)in[a[i].x][a[i].y]+=5; 93 else in[a[i].x][a[i].y]+=2; 94 } 95 } 96 void moveants(){ 97 for(int i=head;i!=tail;i++)moveit(i); 98 } 99 void attack(){ 100 for(int j=1;j<=k;j++)trytoattack(j); 101 for(int i=head;i!=tail;i++)dieant(i); 102 } 103 void judgeend(){ 104 if(cak&&!a[cak].x&&!a[cak].y)lose=true; 105 // & 一开始没看出来......论逻辑与与按位与的差别...... 106 } 107 void endofround(){ 108 /*for(int i=0;i<=n;i++){ 109 for(int j=0;j<=m;j++)printf("%d ",in[i][j]); 110 printf("\n"); 111 }printf("\n");*/ 112 for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)if(in[i][j])in[i][j]--; 113 for(int i=head;i!=tail;i++)a[i].age++; 114 /*for(int i=0;i<=n;i++){ 115 for(int j=0;j<=m;j++)printf("%d ",in[i][j]); 116 printf("\n"); 117 } 118 printf("\n"); 119 for(int i=0;i<=n;i++){ 120 for(int j=0;j<=m;j++)printf("%d ",ant[i][j]); 121 printf("\n"); 122 }*/ 123 } 124 void printwin(){ 125 printf("The game is going on\n"); 126 printf("%d\n",cnt); 127 for(int i=head;i!=tail;i++){ 128 /*if(cak!=i)*/printf("%d %d %d %d %d\n",a[i].age-1,a[i].level,a[i].life,a[i].x,a[i].y); 129 //else printf("id=%d %d %d %d %d %d !!!cake\n",i,a[i].age-1,a[i].level,a[i].life,a[i].x,a[i].y); 130 } 131 } 132 void printlose(){ 133 printf("Game over after %d seconds\n",tim); 134 printf("%d\n",cnt); 135 for(int i=head;i!=tail;i++){ 136 printf("%d %d %d %d %d\n",a[i].age-1,a[i].level,a[i].life,a[i].x,a[i].y); 137 } 138 } 139 void moveit(int i){ 140 //printf("moveit(%d) at(%d,%d)\n",i,a[i].x,a[i].y); 141 bool ok[4]={false},OK=false; 142 int x,y,tmp=0; 143 for(int k=0;k<4;k++){ 144 x=a[i].x+dx[k]; 145 y=a[i].y+dy[k]; 146 ok[k]=can(x,y,i); 147 //printf("x=%d y=%d ok=%d\n",x,y,ok[k]); 148 if(ok[k]){ 149 OK=true; 150 if(in[x][y]>tmp){ 151 //printf("clear ok\n"); 152 memset(ok,false,sizeof(ok)); 153 ok[k]=true; 154 tmp=in[x][y]; 155 } 156 } 157 } 158 //for(int k=0;k<4;k++)printf("%d ",ok[k]);printf("\n"); 159 if(!OK){ 160 moveto(a[i].x,a[i].y,i); 161 return; 162 } 163 if(a[i].age%5){//printf("-"); 164 for(int k=0;k<4;k++)if(ok[k]){ 165 moveto(a[i].x+dx[k],a[i].y+dy[k],i); 166 break; 167 } 168 } 169 else{//This is a bit complex... 170 int k=0;//printf("--"); 171 for(;!ok[k];k++); 172 for(k=(k+3)%4;!can(a[i].x+dx[k],a[i].y+dy[k],i);k=(k+3)%4); 173 moveto(a[i].x+dx[k],a[i].y+dy[k],i); 174 } 175 } 176 bool can(int x,int y,int i){ 177 if(x<0||y<0||x>n||y>m)return false; 178 if(ant[x][y]||tow[x][y])return false; 179 if(x==a[i].lastx&&y==a[i].lasty)return false; 180 return true; 181 } 182 void moveto(int x,int y,int i){//and get cake. 183 //printf("%d from (%d,%d) to (%d,%d)\n",i,a[i].x,a[i].y,x,y); 184 if(x==a[i].x&&y==a[i].y){ 185 a[i].lastx=a[i].lasty=-1; 186 if(!cak&&x==n&&y==m){ 187 //printf("%d gets the cake\n",i); 188 a[i].getcake(); 189 cak=i; 190 } 191 } 192 else{ 193 a[i].lastx=a[i].x; 194 a[i].lasty=a[i].y; 195 ant[a[i].x][a[i].y]=false; 196 ant[x][y]=true; 197 a[i].x=x; 198 a[i].y=y; 199 if(!cak&&x==n&&y==m){ 200 //a[i].havecake=true; 201 //printf("%d gets the cake\n",i); 202 a[i].getcake(); 203 cak=i; 204 } 205 } 206 } 207 void trytoattack(int j){//Tower j trys to attack. 208 //printf("trytoattack(%d)\n",j); 209 int target=0; 210 if(cak&&dcmp(dis(cak,j),r)<=0)target=cak; 211 if(!target){ 212 for(int i=head;i!=tail;i++)if(dcmp(dis(i,j),r)<=0&&(!target||dcmp(dis(i,j),dis(target,j))<0))target=i; 213 } 214 if(!target)return; 215 hitant(target); 216 //printf("hit target(%d)\n",target); 217 for(int i=head;i!=tail;i++)if(i!=target&&dcmp(dis(i,j),dis(target,j))<0&&dcmp(dis3(i,j,target),0.5)<=0){ 218 TOWER A,B; 219 A.x=a[target].x-b[j].x; 220 A.y=a[target].y-b[j].y; 221 B.x=a[i].x-b[j].x; 222 B.y=a[i].y-b[j].y; 223 if(A.x*B.x+A.y*B.y<=0)continue; 224 hitant(i); 225 } 226 } 227 double dis(int i,int j){//The distance of ant i and tower j 228 return sqrt((a[i].x-b[j].x)*(a[i].x-b[j].x)+(a[i].y-b[j].y)*(a[i].y-b[j].y)); 229 } 230 double dis3(int i,int j,int ii){//Ant i's distance to the tower hitting ii 231 TOWER A,B;//In fact these are only two points,2333 232 A.x=a[i].x-b[j].x; 233 A.y=a[i].y-b[j].y; 234 B.x=a[ii].x-a[i].x; 235 B.y=a[ii].y-a[i].y; 236 return abs(A.x*B.y-B.x*A.y)/dis(ii,j); 237 } 238 void hitant(int i){ 239 //printf("%d is hitted\n",i); 240 a[i].life-=d; 241 //if(a[i].life<0)dieant(i); 242 } 243 void dieant(int x){ 244 if(a[x].life>=0)return; 245 //printf("%d die\n",x); 246 if(a[x].havecake)cak=0; 247 ant[a[x].x][a[x].y]=false; 248 swap(a[head],a[x]); 249 head++; 250 cnt--; 251 maintainants(); 252 } 253 void maintainants(){ 254 sort(a+head,a+tail); 255 cak=0; 256 for(int i=head;i!=tail;i++)if(a[i].havecake)cak=i; 257 } 258 int dcmp(double a,double b){ 259 if(fabs(a-b)<=eps)return 0; 260 return a<b?-1:1; 261 } 262 /* 263 8 8 264 2 10 1 265 7 8 266 8 6 267 5 268 Answer: 269 The game is going on 270 5 271 5 1 4 1 4 272 4 1 4 0 4 273 3 1 4 0 3 274 2 1 4 0 2 275 1 1 4 0 1 276 */
16.10.16 Sun.
233333333