权限门
黄学长的题,比较简单的状压DP,考试时写了一个多小时,然后因为一个小错误只拿了40分。
比较明显的是,只有机关墙四周的点是有用的, 有用的点只有4*K个。
我们记DP[S][i][d]表示到过S集合,最后碰到的机关墙是i,停在墙的方向为d。然后通过枚举下一步去哪里、什么方向可以进行转移。
提前预处理出4*K个点两两之间的“转向”最短路,这个通过bfs或SPFA可以搞。我个人偏向写SPFA。
细节不多,注意处理格子出界或是障碍的情况即可。
总时间复杂度:O(预处理+2^K*(4K)^2)。
考试时的错误:一开始机器人的方向随便定了一个,但是应该搞出四个才对。(考试时脑子比较不好使,代码也长得不像话)
#include
#define maxL 102
#define maxn 16
#define INF 0x7FFFFFFF
#define ALL ((1 << K) - 1)
using namespace std;
int n, m, K, ans = INF, Tx, Ty;
char M[maxL][maxL];
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int dddd[] = {1, 3, 0, 2};
struct Key{
int x, y;
}w[maxn];
struct Data{
int x, y, d;
Data() {}
Data(int _x, int _y, int _d):x(_x), y(_y), d(_d) {}
}Q[maxL*maxL];
int Dis[4][maxL][maxL], D[maxn][4][4][maxL][maxL];
bool Vis[4][maxL][maxL];
int Getx(int S, int d){
if(d == 0) return w[S].x - 1;
else if(d == 1) return w[S].x;
else if(d == 2) return w[S].x + 1;
return w[S].x;
}
int Gety(int S, int d){
if(d == 0) return w[S].y;
else if(d == 1) return w[S].y + 1;
else if(d == 2) return w[S].y;
return w[S].y - 1;
}
void SPFA(int S, int ddd){
int sx = Getx(S, ddd), sy = Gety(S, ddd);
if(sx < 1 || sy < 1 || sx > n || sy > m || M[sx][sy-1] == '#') return;
for(int d = 0; d < 4; d++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
Dis[d][i][j] = INF, Vis[d][i][j] = false;
Dis[ddd][sx][sy] = 0;
Vis[ddd][sx][sy] = true;
int hh = 0, tt = 0, mm = maxL * maxL;
Q[hh] = Data(sx, sy, ddd);
while(hh <= tt){
Data now = Q[hh%mm];
hh ++;
int xx = now.x, yy = now.y, d = now.d;
for(int dd = 0; dd < 4; dd++){
if(d == dd) continue;
if(Dis[dd][xx][yy] > Dis[d][xx][yy] + 1){
Dis[dd][xx][yy] = Dis[d][xx][yy] + 1;
if(!Vis[dd][xx][yy]){
Vis[dd][xx][yy] = true;
tt ++;
Q[tt%mm] = Data(xx, yy, dd);
}
}
}
for(int i = 0; i < 4; i++){
int nx = xx + dx[i], ny = yy + dy[i], nd = dddd[i];
if(nx < 1 || ny < 1 || nx > n || ny > m || M[nx][ny-1] == '#') continue;
if(d == nd && Dis[nd][nx][ny] > Dis[d][xx][yy]){
Dis[nd][nx][ny] = Dis[d][xx][yy];
if(!Vis[nd][nx][ny]){
Vis[nd][nx][ny] = true;
tt ++;
Q[tt%mm] = Data(nx, ny, nd);
}
}
}
Vis[d][xx][yy] = false;
}
memcpy(D[S][ddd], Dis, sizeof(Dis));
}
int DP[1<4];
int main(){
freopen("maze.in", "r", stdin);
freopen("maze.out", "w", stdout);
scanf("%d%d%d", &n, &m, &K);
for(int i = 1; i <= n; i++) scanf("%s", M[i]);
int a, b;
for(int i = 1; i <= K; i++){
scanf("%d%d", &a, &b);
w[i].x = a; w[i].y = b;
}
scanf("%d%d", &Tx, &Ty);
for(int i = 1; i <= K; i++)
for(int j = 0; j < 4; j++) SPFA(i, j);
for(int s = 0; s <= ALL; s++)
for(int i = 1; i <= K; i++)
for(int d = 0; d < 4; d++)
DP[s][i][d] = INF;
for(int d = 0; d < 4; d++){
if(d == 0) w[0].x = Tx + 1, w[0].y = Ty;
else if(d == 1) w[0].x = Tx, w[0].y = Ty - 1;
else if(d == 2) w[0].x = Tx - 1, w[0].y = Ty;
else w[0].x = Tx, w[0].y = Ty + 1;
SPFA(0, d);
}
for(int d = 0; d < 4; d++)
for(int i = 1; i <= K; i++){
int ns = 1 << (i-1);
for(int nd = 0; nd < 4; nd++){
int xx = Getx(i, nd), yy = Gety(i, nd);
if(xx < 1 || xx > n || yy < 1 || yy > m || M[xx][yy-1] == '#') continue;
if(D[0][d][nd][xx][yy] == INF) continue;
DP[ns][i][nd] = min(DP[ns][i][nd], D[0][d][nd][xx][yy] + 1);
}
}
for(int s = 0; s < ALL; s++){
for(int i = 1; i <= K; i++)
for(int d = 0; d < 4; d++){
if(DP[s][i][d] == INF) continue;
for(int j = 1; j <= K; j++){
if((1<<(j-1)) & s) continue;
int ns = s | (1 << (j-1));
for(int nd = 0; nd < 4; nd++){
int xx = Getx(j, nd), yy = Gety(j, nd);
if(xx < 1 || xx > n || yy < 1 || yy > m || M[xx][yy-1] == '#') continue;
if(D[i][d][nd][xx][yy] == INF) continue;
DP[ns][j][nd] = min(DP[ns][j][nd], DP[s][i][d] + D[i][d][nd][xx][yy]);
}
}
}
}
for(int i = 1; i <= K; i++)
for(int d = 0; d < 4; d++)
ans = min(ans, DP[ALL][i][d]);
printf("%d\n", ans);
return 0;
}