Link
解题思路
wcnmd,我就无语了,搞了我一天的题,就nm数组开小了,wcnm
守卫监视范围
差分;但我不会,这题数据太淼,直接暴力枚举标记就OK了,在洛谷不行
对于每一个点记录{x,y,c1使用次数,c2使用次数,步数t}
对于一个点,向它能走到的点尝试扩展
- 没有用瞬移
- 新点没有士兵监视,不是士兵,{x + wax[i], y + way[i], c1, c2, t+1}
- 如果新点是士兵监视,但不是士兵,{x + wax[i], y + way[i], c1+1, c2, t+1}
- 用了瞬移
- 新点没有士兵监视,不是士兵,{x + wax[i] * d, y + way[i] * d, c1, c2+1, t+1}
- 如果新点是士兵监视,但不是士兵,{x + wax[i] * d, y + way[i] * d, c1+1, c2+1, t+1}
剪枝
- 走到一个点可能有很多种走法,所以一个点的判重,要判[x][y][c1][c2]有无走过
- 答案的优良性包括{t,c1,c2},so第一次走到终点不一定是最优的,但是如果当前队列层的步数已经超过当前最优答案了,就可以直接退出了
如果方向是偶数,可以尝试瞬移
Code
#include <iostream>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
using namespace std;
struct DT{
int x, y, c1,c2, t;
}now, ans;
const int wax[8] = {
-1, -1, 0, 1, 1, 1, 0, -1}, way[8] = {
0, 1, 1, 1, 0, -1, -1, -1};
queue<DT>q;
string s;
int n, m, c1, c2, d, sx, sy, ex, ey, xx, yy, a[355][355], b[355][355], v[355][355][16][16];
bool check(int x, int y) {
return(x > 0 && y > 0 && x <= n && y <= m); }
void answer(int ax, int ay, int at, int ac1, int ac2) {
if (ax == ex && ay == ey) {
if (at < ans.t || ans.t == -1) {
//比当前答案优,或第一次到达终点
ans.t = at;
ans.c1 = ac1, ans.c2 = ac2;
}else {
if ((ac1 + ac2) < (ans.c1 + ans.c2))//技能数和更少
ans.c1 = ac1, ans.c2 = ac2;
else
if ((ac1 + ac2) == (ans.c1 + ans.c2) && c1 < ans.c1)//隐身更少
ans.c1 = ac1, ans.c2 = ac2;
}
}
}
void bfs() {
v[sx][sy][0][0] = 1, ans.t = -1;
q.push((DT){
sx, sy, 0, 0, 0});
while (!q.empty()) {
now = q.front();
if (now.t > ans.t && ans.t != -1) {
//已经超过当前最优答案了
q.pop();
break;
}
answer(now.x, now.y, now.t, now.c1, now.c2);//尝试更新答案
for (int i = 0; i < 8; i++) {
xx = now.x + wax[i], yy = now.y + way[i];
if (check(xx, yy))//不使用瞬移
if (!a[xx][yy]) {
//不在守卫监视范围——不使用隐身
if (!v[xx][yy][now.c1][now.c2]) {
q.push((DT){
xx, yy, now.c1, now.c2, now.t + 1});
v[xx][yy][now.c1][now.c2] = 1;
}
}else if (!b[xx][yy] && now.c1 < c1){
//在守卫监视范围,不是守卫——使用隐身
if (!v[xx][yy][now.c1 + 1][now.c2]) {
q.push((DT){
xx, yy, now.c1 + 1, now.c2, now.t + 1});
v[xx][yy][now.c1 + 1][now.c2] = 1;
}
}
if (!(i % 2) && now.c2 < c2) {
//使用瞬移,↓同上
xx = now.x + wax[i] * d, yy = now.y + way[i] * d;
if (check(xx, yy))
if (!a[xx][yy]) {
if (!v[xx][yy][now.c1][now.c2 + 1]) {
q.push((DT){
xx, yy, now.c1, now.c2 + 1, now.t + 1});
v[xx][yy][now.c1][now.c2 + 1] = 1;
}
}else if (!b[xx][yy] && now.c1 < c1){
if (!v[xx][yy][now.c1 + 1][now.c2 + 1]) {
q.push((DT){
xx, yy, now.c1 + 1, now.c2 + 1, now.t + 1});
v[xx][yy][now.c1 + 1][now.c2 + 1] = 1;
}
}
}
}
q.pop();
}
}
int main() {
scanf ("%d%d%d%d%d", &n, &m, &c1, &c2, &d);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
cin >> s;
if (s == ".") continue;
if (s == "S") {
sx = i, sy = j;
continue;
}
if (s == "T") {
ex = i, ey = j;
continue;
}
int u = s[0] - 48;
for (int ii = 1; ii < s.size(); ii++)
u = u * 10 + (s[ii] - 48);
for (int ii = max(1, i - u + 1); ii <= min(n, i + u - 1); ii++)
for (int jj = max(1, j - u + 1); jj <= min(m, j + u - 1); jj++)
if ((abs(ii - i) + abs(jj - j)) < u)
a[ii][jj]++;//标记守卫监视范围
b[i][j] = 1;//标记守卫位置
}
bfs();
if (ans.t == -1)
printf ("-1");
else printf ("%d %d %d", ans.t, ans.c1, ans.c2);
}