题意:给一个地图, 地图上有一些字符, 每走到一个地方可以捡取若干个该字符,并且如果有字符的话最少要捡一个, 捡起来的字符放在一个队列里面,然后给一个字符串集合, 问最少要走多少步才能使队列里面最少存在一个给定的字符串。
搞法:
由于不是所有拾取的字符串达到要求,而是有一截满足条件就行, 那么就可以先把字符串集合构成ac自动机, 然后在自动机上跳转状态。
dp[i][j][k] 表示在ac自动机的第i个节点, 地图上的j行k列的最少花费,由于在一个有字符的地方可以有两个选择, 就是跳到下一个位置或者再原地停留, 继续拾取当前的字符, 所以转移就有0和1两种代价, 所以写成最短路形式的dp。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <cstdlib>
using namespace std;
#define LL long long
#define inf 0x3f3f3f3f
#define eps 1e-8
#define ULL unsigned long long
#define mnx 420
#define mxe 10020
#define mxnode 20020
#define mod 1000000007
char g[22][22];
int n, m, cnt;
char s[mnx];
int dp[20020][22][22];
int sx, sy;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
bool in(int x, int y) {
return x >= 1 && y >= 1 && x <= n && y <= m;
}
bool inq[20020][22][22];
struct trie {
int ch[mxnode][26];
bool mark[mxnode];
int f[mxnode], sz;
int creat() {
memset(ch[sz], -1, sizeof(ch[sz]));
mark[sz] = 0;
return sz++;
}
void init() {
sz = 0, creat();
}
void insert(char *s) {
int t = 0;
for(int i = 0; s[i]; ++i) {
int c = s[i] - 'A';
if(!~ch[t][c]) ch[t][c] = creat();
t = ch[t][c];
}
mark[t] = 1;
}
void build() {
queue<int> q; f[0] = 0;
for(int i = 0; i < 26; ++i) {
int &v = ch[0][i];
if(!~v) v = 0;
else f[v] = 0, q.push(v);
}
while(!q.empty()) {
int t = q.front(); q.pop();
if(mark[f[t]]) mark[t] = 1; //注意要不要加这行
for(int i = 0; i < 26; ++i) {
int &v = ch[t][i];
if(!~v) v = ch[f[t]][i];
else {
f[v] = ch[f[t]][i];
q.push(v);
}
}
}
}
int spfa() {
int ret = inf;
memset(inq, 0, sizeof inq);
memset(dp, 0x3f, sizeof dp);
dp[0][sx][sy] = 0;
queue<int> q;
q.push(0), q.push(sx), q.push(sy);
while(!q.empty()) {
int s = q.front(); q.pop();
int x = q.front(); q.pop();
int y = q.front(); q.pop();
inq[s][x][y] = 0;
for(int i = 0; i < 4; ++i) {
int dx = x + dir[i][0];
int dy = y + dir[i][1];
if(!in(dx, dy)) continue;
if(g[dx][dy] == '#') continue;
if(g[dx][dy] == '.') {
if(dp[s][dx][dy] <= dp[s][x][y] + 1) continue;
dp[s][dx][dy] = dp[s][x][y] + 1;
if(!inq[s][dx][dy])
inq[s][dx][dy] = 1, q.push(s), q.push(dx), q.push(dy);
continue;
}
int t = ch[s][g[dx][dy]-'A'];
if(dp[t][dx][dy] <= dp[s][x][y] + 1) continue;
dp[t][dx][dy] = dp[s][x][y] + 1;
if(!inq[t][dx][dy])
inq[t][dx][dy] = 1, q.push(t), q.push(dx), q.push(dy);
}
if(g[x][y] != '.') {
int t = ch[s][g[x][y]-'A'];
if(dp[t][x][y] > dp[s][x][y]) {
dp[t][x][y] = dp[s][x][y];
if(!inq[t][x][y])
inq[t][x][y] = 1, q.push(t), q.push(x), q.push(y);
}
}
}
for(int i = 1; i < sz; ++i) {
if(mark[i] == 0) continue;
for(int j = 1; j <= n; ++j)
for(int k = 1; k <= m; ++k)
ret = min(ret, dp[i][j][k]);
}
if(ret == inf) ret = -1;
return ret;
}
}ac;
int main() {
int cas;
scanf("%d", &cas);
while(cas--) {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
scanf("%s", g[i] + 1);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
if(g[i][j] == '@') {
sx = i, sy = j, g[i][j] = '.';
break;
}
ac.init();
int q;
scanf("%d", &q);
while(q--) {
scanf("%s", s);
ac.insert(s);
}
ac.build();
printf("%d\n", ac.spfa());
}
return 0;
}