ZOJ-3698 Carrot Fantasy 恶心模拟
题意:给定了一个塔防的游戏模型,跟着这个模型进行模拟,输出能在多少时间内消灭掉所有的怪兽,如果不能的话,输出-1。
解法:这里提几个要注意的地方:
1.注意怪兽不要走回头路;
2.注意已死的怪兽不能够再次成为防御塔选择或者是移动的对象;
3.所有防御塔是同时攻击的,因此选择型的防御塔不能够预判出该秒攻击是否多余,即最优目标是绝对的,不会因为其他塔能够射杀死中途改变;
4.要注意最后全部怪兽被持续冻结而不掉血的情况。
题目中可能没有把这个游戏的过程说的特别详细,对于每一秒钟是如下度过的:
在一秒钟开始的时候(S)所有的怪兽全部获得一秒钟更新时间:如果怪兽被冻结,那么这一秒钟用于解冻,无法移动;否则使用一秒钟向前移动一步。如果怪兽中毒的话,这一秒钟末(T)将掉一次血,同时在一秒末(T)获得所有炮塔的一次攻击选择。
由于所有的怪兽第一次出生无法移动,所以我们选择在第 i 秒末被炮塔攻击前出生,这样既能够被火力覆盖,又能够在下一秒移动了。对于是否被永久冻结的判定,只要是对游戏结束有推动作用的就认为没有陷入无限循环之中,包括有怪兽移动,怪兽中毒掉血,刚刷新出来的怪兽,被炮塔攻击后掉血,怪兽中毒这些。注意解冻和冻结不认为推动了游戏的结束,因为如果单单只是解冻和冻结游戏永远无法结束,而前面列举的动作只要有某个(有些状态是关联的,例如有一直怪兽中毒,肯定就会有中毒掉血,除非每次都被炮火无情摧残,不等到其中毒掉血,但这又使得怪兽中毒与其他相关联)连续作用,怪兽的数量是有限的,只要一直进行那么游戏总会以某种方式结束。
代码如下:
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <cctype> #include <algorithm> using namespace std; int sx, sy, ex, ey; // 用来表示怪兽起点和终点的坐标 int N, M, K, H; struct monsters { int hp, x, y, step; // 分别表示怪兽的血量,坐标x,y,走了多少步 int dir, fz, po; // 上一步的方向以及是否冻结或中毒 void init() { hp = H, x = sx, y = sy, step = 0; fz = po = 0, dir = -1; } }; struct tower { int id, x, y; int obj; // 用来记录每一时刻在攻击范围内的怪兽编号 void init(int Id, int X, int Y) { id = Id, x = X, y = Y, obj = -1; } }; int dir[4][2] = {0,1,0,-1,-1,0,1,0}; int ff[8][2] = {0,1,0,-1,-1,0,1,0,1,1,1,-1,-1,1,-1,-1}; char mp[55][55]; monsters m[55]; tower t[55][55]; bool legal(int x, int y) { if (x < 1 || x > N || y < 1 || y > M) return false; return true; } int cmp(int a, int b) { if (a == -1) return b; if (b == -1) return a; if (m[a].step != m[b].step) { if (m[a].step > m[b].step) { // 走的步数多的怪兽被攻击的优先级高 return a; } else return b; } else { if (a < b) { // 否则放回编号较小的怪兽 return a; } else return b; } } /* 123 4 4 10 10 S... XXI. XXX. T... 2 2 2 20 ST FN */ bool over(int x) { if (x < K) return false; // 如果还有怪兽没有出生 for (int i = 1; i <= x; ++i) { if (m[i].hp > 0) return false; // 如果还有怪物没有死亡 } return true; } void solve() { int many = 0, ti; bool fail = false; for (ti = 1; !over(many) && !fail; ++ti) { bool change = false; for (int i = 1; i <= many; ++i) { // 首先给地图上的怪兽都分配一秒钟的时间 if (m[i].hp <= 0) continue; // 如果这个怪兽已经死亡 if (m[i].po) { m[i].hp -= 10; change = true; // 如果有怪兽中毒扣血,则说明有更新 if (m[i].hp <= 0) { continue; } } if (m[i].fz) { m[i].fz = 0; // 如果被冻结,则解冻,该秒钟无法行进 } else { // 如果没有冻结则要前进一个距离 m[i].step += 1; // 该怪兽前进的步伐加1 change = true; // 只要还在走,说明有更新 for (int j = 0; j < 4; ++j) { if (j == m[i].dir) continue; // 保证它不能够往原路返回 int cx = m[i].x + dir[j][0], cy = m[i].y + dir[j][1]; if (legal(cx, cy) && (mp[cx][cy] == '.' || mp[cx][cy] == 'T')) { // 如果这个方向是正确的方向 m[i].x = cx, m[i].y = cy, m[i].dir = j ^ 1; break; } } if (m[i].x == ex && m[i].y == ey) { fail = true; break; } } } if (fail) continue; if (ti <= K) { // 少于K的时间内,每秒刷新一个新怪兽,且该怪兽该秒钟无法移动,但是能够成为攻击目标 m[ti].init(); change = true; } many = min(ti, K); for (int i = 1; i <= many; ++i) { // 选择用怪兽匹配炮塔,因为炮塔是固定且不重叠 if (m[i].hp <= 0) continue; // 已经死亡的不予考虑,这个死亡是以前的攻击造成的 for (int j = 0; j < 8; ++j) { // 在这8次匹配中可能中途死亡,由于是同时攻击,所以不用中途退出 int cx = m[i].x + ff[j][0], cy = m[i].y + ff[j][1]; if (legal(cx, cy) && isdigit(mp[cx][cy])) { // 该怪兽在某个炮塔的火力覆盖范围内 if (t[cx][cy].id != 2) { t[cx][cy].obj = cmp(t[cx][cy].obj, i); // 选择性炮塔只要选择一个最优的进行攻击 } else { // 如果是Fire Tower,只要在其火力覆盖内,则直接掉血 m[i].hp -= 10; change = true; } } } } for (int i = 1; i <= N; ++i) { for (int j = 1; j <= M; ++j) { if (t[i][j].id && t[i][j].id != 2 && t[i][j].obj != -1) { // 如果该地点有炮塔 if (t[i][j].id == 1) { change = true; m[t[i][j].obj].hp -= 10; } else if (t[i][j].id == 3) { change = true; // 有中毒就又希望,如果一直有怪兽中毒,不管它之前有没有中毒,如果到不了终点,它最终将死掉 m[t[i][j].obj].po = 1; } else { // 被冻结或者解冻不能算是有更新,因为单纯的这个过程怪兽没有掉血也没有移动 m[t[i][j].obj].fz = 1; } t[i][j].obj = -1; // 将对象释放 } } } if (!change) fail = true; } if (fail) puts("-1"); else printf("%d\n", ti - 1); } int main() { int T; for (scanf("%d", &T); T; --T) { scanf("%d %d %d %d", &N, &M, &K, &H); for (int i = 1; i <= N; ++i) { scanf("%s", mp[i]+1); for (int j = 1; j <= M; ++j) { t[i][j].id = 0; // 默认该地点没有炮塔 if (mp[i][j] == 'X' || mp[i][j] == '.') continue; else if (mp[i][j] == 'B') mp[i][j] = '1', t[i][j].init(1, i, j); else if (mp[i][j] == 'F') mp[i][j] = '2', t[i][j].init(2, i, j); else if (mp[i][j] == 'N') mp[i][j] = '3', t[i][j].init(3, i, j); else if (mp[i][j] == 'I') mp[i][j] = '4', t[i][j].init(4, i, j); else if (mp[i][j] == 'S') sx = i, sy = j; else ex = i, ey = j; } } solve(); } return 0; }