XJOI 9728:迷宫难题 题解

题目描述:

这是一个 nm 列的迷宫,某些格子有若干枚钥匙。相邻格子之间有一扇门,门上有若干把锁,所有的锁都打开才能够通行,若没有锁则直接通行。对应的钥匙可以打开对应的锁,钥匙和锁的类型范围由数字 0p 表示。每一步可以走到周围四连通的格子之一,门可以从两个方向打开,捡钥匙和开锁不消耗时间。问从 (1, 1)(n, m) 最少需要走多少步。

输入格式:

第一行三个整数 n,m,p

第二行一个整数 K

接下来 K 行每行 5 个整数 x1, y1, x2, y2, t ,表示 (x1, y1)(x2, y2) 之间的门上有一把 t 类型的锁。

保证 (x1, y1)(x2, y2) 相邻。

下一行一个整数 S ,表示钥匙的个数。

接下来 S 行每行三个整数 x, y, t ,表示 x,y 处有一枚 t 类型的钥匙。

输出格式:

输出一行表示答案。

样例输入:

4 4 9 
9 
1 2 1 3 2 
1 2 2 2 0 
2 1 2 2 0 
2 1 3 1 0 
2 3 3 3 0 
2 4 3 4 1 
3 2 3 3 0 
3 3 4 3 0 
4 3 4 4 0 
2 
2 1 2 
4 2 1

样例输出:

14

数据规模:

N,M,P≤10,K<150,S≤150

思路

状态压缩:将钥匙的拥有情况表示为二进制下第 k 位的 01 。有就是1,无就是0
例如有0,2,3钥匙,则二进制表示为 1101

然后就是bfs了。以下是几个位运算的小技巧

  • s & 1 << k > 0 :二进制下第 k 位是不是1。
  • s | = 1 << k :二进制下将第 k 位变为1。
  • (s&t)==t :检查二进制下 s 中是否包含 t

位运算YYDS!以上操作均可以在十进制下进行。

状态设计dis[x][y][k] 表示在拥有k表示的钥匙的情况下到x,y的最短距离。 vis[x][y][k] 表示是否在拥有k表示的钥匙串的情况下经过 (x,y) 。最后如何求出最小的距离呢?只需要遍历 k (从 02^{11} )然后找最小的 dis[n][m][k] 。发现答案还是初始值说明无法到达呗,输出 -1

AC Code

#include <bits/stdc++.h>
using namespace std;
struct node{
    int x,y,k;
};
queue<node> q;
int n,m,p,k,s,x,y;
bool vis[15][15][2050];
int door[15][15][15][15],key[15][15],dis[15][15][2050];
int d[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int main(){
    scanf("%d%d%d",&n,&m,&p);
    scanf("%d",&k);
    for(int i=1;i<=k;i++){
        int x1,x2,y1,y2,t;
        scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &t);
        door[x1][y1][x2][y2] |= (1 << t);
        door[x2][y2][x1][y1] |= (1 << t);
    }
    scanf("%d",&s);
    for(int i=1;i<=s;i++){
        int x,y,t;
        scanf("%d%d%d",&x,&y,&t);
        key[x][y] |= (1 << t);
    }
    memset(dis,0x3f,sizeof(dis));
    dis[1][1][key[1][1]]=0;
    vis[1][1][key[1][1]]=true;
    q.push((node){1,1,key[1][1]});
    while(!q.empty()){
        node now=q.front();
        q.pop();
        int x=now.x,y=now.y,k=now.k;
        for(int i=0;i<4;i++){
            int nx=x+d[i][0],ny=y+d[i][1];
            if(nx>=1 && nx<=n && ny>=1 && ny<=m && (k&door[x][y][nx][ny])==door[x][y][nx][ny]) {
                int nk=k|key[nx][ny];
                if(!vis[nx][ny][nk]){
                    vis[nx][ny][nk]=true;
                    dis[nx][ny][nk]=dis[x][y][k]+1;
                    q.push((node){nx,ny,nk});
                }
            }
        }
    }
    int ans=dis[0][0][0];
    for(int i=0;i<2048;i++){
        ans=min(ans,dis[n][m][i]);
    }
    if(ans==dis[0][0][0]) cout<<"-1"<<endl;
    else cout<<ans<<endl;
    return 0;
}
5 个赞

tqltqltql%%%

1 个赞