2014
02-14

# Zerg Rush!!!

A typical strategy in the game Starcraft is to mass up large amounts of low-tier units such as Zerglings, then throw them at your opponent and laugh maniacally as they overwhelm any opposition. However, when both players opt for the same strategy, the result can often become…quick, brutal and messy. Sadly, the game only allows for up to 400 Zerglings per player. In this problem, however, you will simulate games that could have more than 400 Zerglings.

The map on which Zerg rushes occur is represented by a NxN grid. Each Zergling occupies a grid square and no two Zerglings ever occupy the same grid square. Each Zergling has a certain number of hit points which starts off as 35. Its attack value is 5 plus the attack upgrade of the player that controls it. When one Zergling attacks another, the damage incurred equals to the attack value of the attacking Zergling minus the armour upgrade of the player that owns the Zergling being attacked. The number of hit points of the Zergling that is attacked is reduced by the amount of the damage.

Due to the inability of both players to manage their huge horde of Zerglings, the Zerglings make their decisions each turn using the following algorithm (Zerglings are not the brightest creatures, as you will see):

* If there is an opponent Zergling in one of the 8 horizontally, vertically, or diagonally adjacent grid squares, the Zergling will attack it. A Zergling attacks at most one opponent each turn; see below for the tie-breaking rules.
* Otherwise, if the other player has at least one Zergling remaining on the map, the Zergling will move to the horizontally, vertically, or diagonally adjacent square that is closest to the opponent’s closest Zergling in terms of Manhattan distance. When more than one adjacent square is closest, the tie-breaking rules below are used. The Manhattan distance between two points is the sum of the differences in the x and y coordinates of the points.

When the above rules could cause the Zergling to attack in more than one direction, or to move in more than one direction, the following tie-breaking rule is used. The Zergling will prefer the first direction starting with north going clockwise. That is, the directions in order of preference are north, northeast, east, southeast, etc.

Once all Zerglings have made their decisions, all the attacks are conducted simultaneously and all the Zerglings with 0 or fewer hit points are marked as dead and removed from the map. Then all the movements of the Zerglings that didn’t attack are conducted simultaneously. If the square to which a Zergling is moving is occupied by another Zergling that is not moving away in this turn, then the Zergling does not move. If two or more Zerglings try to move to the same grid square, then the Zergling in the northernmost row has the right of way and the other Zergling remains stationary. If there are multiple Zerglings in the northernmost row trying to move to the same grid square, then of these, the westernmost Zergling moves and the others remain stationary. Zerglings also have a remarkable regeneration rate. After each turn, all the Zerglings that are still alive and have less than 35 hitpoints will regain one hit point.

The input file contains a number of test cases, ended by a case with N=0. Each case begins with N between 2 and 150, followed by 2 pairs of 2 integers between 0 and 3, the attack and armour upgrades of the first player, followed by the attack and armour upgrades of the second player. This is followed by the initial game map, where ‘.’ denotes an empty square, ’1′ a Zergling belonging to the first player and ’2′ a Zergling belonging to the second player. On the map, north is up (i.e. towards the first row) and west is left (i.e. towards the first column). Finally, the test case provides the number t of turns for which the Zerg rush is to be simulated, which is an integer between 0 and 400, inclusive.

The input file contains a number of test cases, ended by a case with N=0. Each case begins with N between 2 and 150, followed by 2 pairs of 2 integers between 0 and 3, the attack and armour upgrades of the first player, followed by the attack and armour upgrades of the second player. This is followed by the initial game map, where ‘.’ denotes an empty square, ’1′ a Zergling belonging to the first player and ’2′ a Zergling belonging to the second player. On the map, north is up (i.e. towards the first row) and west is left (i.e. towards the first column). Finally, the test case provides the number t of turns for which the Zerg rush is to be simulated, which is an integer between 0 and 400, inclusive.

2
0 0
0 0
1.
..
0
2
0 0
0 0
1.
.2
100
0

1.
..

..
..

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <sstream>

using namespace std;
typedef long long ll;
#define REP(i,s,t) for(int i=(s);i<(t);i++)
#define FILL(x,v) memset(x,v,sizeof(x))
#define FOREACH(i,v) for(typeof((v).begin()) i=(v).begin();i!=(v).end();i++)
int N;
#define IN(x) ((x)>=1 && (x)<=N)
#define MP0 make_pair(0,0)
#define MAXN 22555
const int INF = (int)1E9;
int atk1, atk2, def1, def2;
int n1, n2;
struct zerg{
int hp;
int x, y, a;
zerg(){}
zerg(int _x, int _y){
hp = 35;
x = _x; y = _y;
a = 0;
}
} z1[MAXN], z2[MAXN];

char gd[200];
pair<int,int> fd[200][200], fdnxt[200][200], from[200][200];
int dst1[200][200], dst2[200][200];

bool v[200][200];
struct dij{
int x, y, d;
dij(int _x, int _y, int _d){
x = _x; y = _y; d = _d;
}
};

int dir8[8][2] = {-1,0,-1,1,0,1,1,1,1,0,1,-1,0,-1,-1,-1};
int dir4[4][2] = {-1,0,0,1,1,0,0,-1};

void repack(int x, int y, int thatg, int thatid){
if(fdnxt[x][y].first==0){
zerg *z = thatg==1? z1:z2;
z[thatid].x = x;
z[thatid].y = y;
fdnxt[x][y] = make_pair(thatg, thatid);
//from[x][y] = MP0;
return;
}
//if(from[x][y]==MP0) return;
repack(from[x][y].first, from[x][y].second, fdnxt[x][y].first, fdnxt[x][y].second);
from[x][y] = MP0;
fdnxt[x][y] = make_pair(thatg, thatid);
zerg *z = thatg==1? z1:z2;
z[thatid].x = x;
z[thatid].y = y;
}

void bfs(zerg *z, int n, int dst[200][200]){
//REP(i,1,N+1) REP(j,1,N+1) dst[i][j] = INF;
FILL(v, 0);
queue<dij> que;
REP(i,0,n){
que.push(dij(z[i].x, z[i].y, 0));
v[z[i].x][z[i].y] = 1;
dst[z[i].x][z[i].y] = 0;
}
while(!que.empty()){
dij now = que.front(); que.pop();
REP(i,0,4){
int nx = now.x + dir4[i][0],
ny = now.y + dir4[i][1];
if(IN(nx) && IN(ny) && !v[nx][ny]){
dst[nx][ny] = now.d + 1;
v[nx][ny] = 1;
que.push(dij(nx,ny,now.d+1));
}
}
}
}

int main(){
bool fst = 1;
while(1){
scanf("%d", &N);
if(!N) break;

if(fst) fst = 0;
else puts("");

scanf("%d%d%d%d", &atk1, &def1, &atk2, &def2);
n1 = n2 = 0;

FILL(fd, 0);

REP(i,1,N+1){
scanf("%s", &gd[1]);
REP(j,1,N+1){
if(gd[j]=='1'){
fd[i][j] = make_pair(1, n1);
z1[n1++] = zerg(i,j);
}else if(gd[j]=='2'){
fd[i][j] = make_pair(2, n2);
z2[n2++] = zerg(i,j);
}
}
}
int T;
scanf("%d", &T);
REP(t,1,T+1){
REP(i,0,n1) z1[i].a = 0;
REP(i,0,n2) z2[i].a = 0;
// atk zerg1
REP(i,0,n1){
int x = z1[i].x, y = z1[i].y;
REP(d,0,8){
int nx = x+dir8[d][0], ny = y+dir8[d][1];
if(fd[nx][ny].first==2){
int id = fd[nx][ny].second;
z2[id].hp -= 5+atk1-def2;
z1[i].a = 1;
break;
}
}
}
// atk zerg2
REP(i,0,n2){
int x = z2[i].x, y = z2[i].y;
REP(d,0,8){
int nx = x+dir8[d][0], ny = y+dir8[d][1];
if(fd[nx][ny].first==1){
int id = fd[nx][ny].second;
z1[id].hp -= 5+atk2-def1;
z2[i].a = 1;
break;
}
}
}

bfs(z2, n2, dst1);
bfs(z1, n1, dst2);

REP(i,0,n1){
int x = z1[i].x, y = z1[i].y;
if(z1[i].hp<=0) {
fd[x][y] = MP0;

int x2 = z1[n1-1].x, y2 = z1[n1-1].y;
fd[x2][y2].second = i;

swap(z1[i], z1[n1-1]);
n1--;
i--;
}
}
REP(i,0,n2){
int x = z2[i].x, y = z2[i].y;
if(z2[i].hp<=0){
fd[x][y] = MP0;

int x2 = z2[n2-1].x, y2 = z2[n2-1].y;
fd[x2][y2].second = i;

swap(z2[i], z2[n2-1]);
n2--;
i--;
}
}

FILL(fdnxt, 0);
FILL(from, 0);
REP(i,0,n1) if(z1[i].a==1) fdnxt[z1[i].x][z1[i].y] = make_pair(1, i);
REP(i,0,n2) if(z2[i].a==1) fdnxt[z2[i].x][z2[i].y] = make_pair(2, i);
REP(i,0,n1) if(z1[i].hp<35) z1[i].hp++;
REP(i,0,n2) if(z2[i].hp<35) z2[i].hp++;

// move zerg
REP(i,1,N+1){
REP(j,1,N+1){
int g = fd[i][j].first,
id = fd[i][j].second;
if(g==0) continue;
zerg *z = g==1?z1:z2;
if(z[id].a) continue;
int x = z[id].x, y = z[id].y;

int ad = -1, mind = INF;
REP(d,0,8){
int nx = x + dir8[d][0],
ny = y + dir8[d][1];
if(IN(nx) && IN(ny) && (g==1?dst1[nx][ny]:dst2[nx][ny])<mind){
mind = g==1?dst1[nx][ny]:dst2[nx][ny];
}
}
int nx = x + dir8[ad][0],

if(fdnxt[nx][ny].first==0){
fdnxt[nx][ny] = make_pair(g, id);
from[nx][ny] = make_pair(x,y);
z[id].x = nx;
z[id].y = ny;
}else{
// someone else moves here, not move
repack(x,y,g,id);
}
}
}
memcpy(fd, fdnxt, sizeof(fd));
if(n1==0 || n2==0) break;
}

REP(i,1,N+1){
REP(j,1,N+1){
if(fd[i][j].first){
printf("%d", fd[i][j].first);
}else printf(".");
}
puts("");
}
}
return 0;
}

1. 思路二可以用一个长度为k的队列来实现，入队后判断下队尾元素的next指针是否为空，若为空，则出队指针即为所求。