首先因为点数不多,可以考虑先在 $4r \times c$ 时间内将每个点朝每个方向可以到达的位置预处理出来,建成图

那么现在容易看出是斯坦那树问题,所以考虑将问题简化一下:

现在给定 $n$ 个不同级别的机器人,将两段级别的机器人合并需要花费 $c_i$,那么将所有机器人合并的最小花费是多少

这个问题显然使用区间 $DP$ 可以解决,那么扩展到斯坦那树上即可

令 $f[l][r][i]$ 表示区间 $l, r$ 的机器人合并后位于位置 $i$ 的最小花费,则有
$$
f[l][r][i] = \min \{f[l][k][i] + f[k + 1][r][i]\}
$$
以及
$$
f[l][r][i] = \min\limits_{p link to i} \{f[l][r][p] + 1\}
$$
第二个式子直接 $SPFA$ 会超时

但是观察每条边的贡献都为 $1$,所以可以将 $SPFA$ 优化成 $BFS$ 的复杂度

类似堆优化的思想,开两个队列,一个存初始的经过 $f$ 排序的点,一个存被松弛的点,由于边的性质及 $que1$ 经过排序的原因,所以 $que2$ 一定也是按 $f$ 递增的,从而优化成 $BFS$ 的复杂度

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

const int MAXN = 9 + 5;
const int MAXL = 500 + 10;
const int MAXI = MAXL * MAXL;
const int MAXM = 4 * MAXI;

const int INF = 0x3f3f3f3f;

const int NextX[4]= {- 1, 0, 1, 0}, NextY[4]= {0, - 1, 0, 1};

struct LinkedForwardStar {
int to;

int next;
} ;

LinkedForwardStar Link[MAXM];
int Head[MAXI]= {0};
int size = 0;

void Insert (int u, int v) {
Link[++ size].to = v;
Link[size].next = Head[u];

Head[u] = size;
}

int N, R, C;
char Map[MAXL][MAXL];

int encode (int x, int y) {
return (x - 1) * C + y;
}

int fto[MAXL][MAXL][4]= {0};
bool vis[MAXL][MAXL][4]= {false};
int move (int x, int y, int k) {
if (vis[x][y][k])
return fto[x][y][k] = - 1;
if (fto[x][y][k])
return fto[x][y][k];
int tx = x + NextX[k], ty = y + NextY[k];
vis[x][y][k] = true;
if (Map[tx][ty] == 'x')
fto[x][y][k] = encode (x, y);
else if (Map[tx][ty] == 'A')
fto[x][y][k] = move (tx, ty, (k + 1) % 4);
else if (Map[tx][ty] == 'C')
fto[x][y][k] = move (tx, ty, (k + 3) % 4);
else
fto[x][y][k] = move (tx, ty, k);
vis[x][y][k] = false;
return fto[x][y][k];
}

int f[MAXN][MAXN][MAXI];
queue<int> que1, que2;
int inque[MAXI]= {0};
int buck[MAXI]= {0};
int save[MAXI];
int total = 0;
void fsort (int l, int r) {
total = 0;
int limit = 0;
for (int i = 1; i <= R * C; i ++)
if (f[l][r][i] < INF)
limit = max (limit, f[l][r][i]), total ++;
for (int i = 1; i <= limit; i ++)
buck[i] = 0;
for (int i = 1; i <= R * C; i ++)
if (f[l][r][i] < INF)
buck[f[l][r][i]] ++;
for (int i = 1; i <= limit; i ++)
buck[i] += buck[i - 1];
for (int i = R * C; i >= 1; i --)
if (f[l][r][i] < INF)
save[buck[f[l][r][i]] --] = i;
}
void SPFA (int l, int r) {
fsort (l, r);
for (int i = 1; i <= total; i ++)
que1.push(save[i]), inque[save[i]] = 1;
while (! que1.empty() || ! que2.empty()) {
int u;
int f1 = ! que1.empty() ? que1.front() : INF;
int f2 = ! que2.empty() ? que2.front() : INF;
f1 < f2 ? (u = f1, que1.pop()) : (u = f2, que2.pop());
inque[u] = 0;
for (int i = Head[u]; i; i = Link[i].next) {
int v = Link[i].to;
if (f[l][r][u] + 1 < f[l][r][v]) {
f[l][r][v] = f[l][r][u] + 1;
if (! inque[v])
que2.push(v), inque[v] = 1;
}
}
}
}

int main () {
scanf ("%d%d%d", & N, & C, & R);
for (int i = 1; i <= R; i ++)
scanf ("%s", Map[i] + 1);
for (int i = 1; i <= R; i ++)
Map[i][0] = Map[i][C + 1] = 'x';
for (int j = 1; j <= C; j ++)
Map[0][j] = Map[R + 1][j] = 'x';
for (int i = 1; i <= R; i ++)
for (int j = 1; j <= C; j ++) {
if (Map[i][j] == 'x')
continue;
for (int k = 0; k < 4; k ++) {
move (i, j, k);
if ((~ fto[i][j][k]) && fto[i][j][k] != encode (i, j))
Insert (encode (i, j), fto[i][j][k]);
}
}
memset (f, 0x3f, sizeof (f));
for (int i = 1; i <= R; i ++)
for (int j = 1; j <= C; j ++)
if (isdigit (Map[i][j]))
f[Map[i][j] - '0'][Map[i][j] - '0'][encode (i, j)] = 0;
for (int len = 1; len <= N; len ++)
for (int l = 1; l <= N - len + 1; l ++) {
int r = l + len - 1;
for (int i = 1; i <= R * C; i ++) {
for (int k = l; k < r; k ++)
f[l][r][i] = min (f[l][r][i], f[l][k][i] + f[k + 1][r][i]);
}
SPFA (l, r);
}
int ans = INF;
for (int i = 1; i <= R * C; i ++)
ans = min (ans, f[1][N][i]);
printf ("%d\n", ans == INF ? - 1 : ans);

return 0;
}

/*
4 10 5
1.........
AA...x4...
..A..x....
2....x....
..C.3.A...
*/