《杀蚂蚁》有感

昨天下午给别人调了一会儿杀蚂蚁(炸精度了),于是乎自己心血来潮也想写写这道神题。

写代码没费多久,主要都是调代码。调了三节课,时间这么短的原因是因为我是对着标程调的(直接把下午调过了的那份代码输出调试信息然后两边手动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 */
View Code

16.10.16 Sun.

posted @ 2016-10-16 07:22  AntiLeaf  阅读(211)  评论(0编辑  收藏  举报