[NOIP模拟赛][分层图][状态压缩] 密室
题面
题目描述
小 \(X\) 正困在一个密室里,他希望尽快逃出密室。
密室中有 \(N\) 个房间,初始时,小 \(X\) 在 \(1\) 号房间,而出口在 \(N\) 号房间。
密室的每一个房间中可能有着一些钥匙和一些传送门,一个传送门会单向地创造一条从房间 \(X\) 到房间 \(Y\) 的通道。另外,想要通过某个传送门,就必须具备一些种类的钥匙(每种钥匙都要有才能通过)。幸运的是,钥匙在打开传送门的封印后,并不会消失。
然而,通过密室的传送门需要耗费大量的时间,因此,小 \(X\) 希望通过尽可能少的传送门到达出口,你能告诉小 \(X\) 这个数值吗?
另外,小 \(X\) 有可能不能逃出这个密室,如果是这样,请输出 \(No\ Solution\)。
我们可以利用分层图的思想,将当前获得的钥匙有哪些作为分层的依据进行转移,
我们发现 \(k \lt 10\),想到可以状态压缩表示钥匙数,
然后这题就差不多解决了
代码:
// 状压二进制枚举当前钥匙的状态
// BFS 转移 Dis
# include <iostream>
# include <cstdio>
# include <cstring>
# include <queue>
# define MAXN 6005
# define MAXM 6005
struct edge{
int v, next, key;
}e[MAXM];
struct node{
int id, key;
};
int hd[MAXN], cntE; int dis[MAXN]; bool inQ[1<<11][MAXN];
int hasKey[MAXN]; // 表示每个节点有的钥匙
// int reqKey[MAXM]; // 表示每个传送门需要的钥匙
// if(nowKey & reqKey == reqKey) then
int f[1<<11][MAXN];
void AddE(int u, int v, int reqKey){
e[++cntE] = (edge){v, hd[u], reqKey};
hd[u] = cntE;
}
int main(){
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= n; i++){
for(int j = 1, x; j <= k; j++){
hasKey[i] <<= 1;
scanf("%d", &x);
hasKey[i] += x;
}
}
for(int i = 1, x, y, req; i <= m; i++){
req = 0;
scanf("%d%d", &x, &y);
for(int j = 1, xx; j <= k; j++){
req <<= 1;
scanf("%d", &xx);
req += xx;
}
AddE(x, y, req);
}
std::queue<node>Q;
memset(f, 0x3f, sizeof(f));
f[hasKey[1]][1] = 0; Q.push((node){1, hasKey[1]});
inQ[hasKey[1]][1] = 1;
while(Q.size()){
node now = Q.front(); Q.pop();
inQ[now.key][now.id] = 0;
for(int i = hd[now.id], nxtSta; i; i = e[i].next){
nxtSta = now.key | hasKey[e[i].v];
if((e[i].key & now.key) == e[i].key && f[nxtSta][e[i].v] > f[now.key][now.id] + 1){
f[nxtSta][e[i].v] = f[now.key][now.id] + 1;
if(!inQ[nxtSta][e[i].v]){
inQ[nxtSta][e[i].v] = 1;
Q.push((node){e[i].v, nxtSta});
}
}
}
}
int ans = 0x3f3f3f3f;
for(int i = 0; i < (1<<k); i++){
ans = std::min(ans, f[i][n]);
}
if(ans == 0x3f3f3f3f){
printf("No Solution");
}
else{
printf("%d", ans);
}
return 0;
}