传送门 //因为一本通上有一个测试点错了,所以给洛谷的测试
这题大部分都是用bfs + 状压去做,但是一本通里把这题放在了最短路上,虽然用最短路的时间空间都比bfs要差,但是这是一道很好的去训练分层最短路的题。
BFS版:
bfs把每个种类的钥匙都单独分成一层,然后根据我拿了钥匙的状态去找到我现在应该在的层,再bfs搜索最短路。
#include
using namespace std;
const int N = 12;
int dir[5][3]={{0,1},{1,0},{0,-1},{-1,0}};
int e[N][N][N][N],key[N][N][N],dkey[N][N];
int vis[N][N][1<<14];
int n,m;
struct node{
int x,y,k,d;
node(){}
node(int x,int y,int k,int d):x(x),y(y),k(k),d(d){}
};
int getkey(int x,int y){
int ans = 0;
for(int i=1;i<=dkey[x][y];i++)
ans|=(1<<(key[x][y][i]-1));
return ans;
}
int bfs(){
queue q;
int sk = getkey(1,1);
q.push(node(1,1,sk,0)),vis[1][1][sk] = 1;
while(q.size()){
node u = q.front();q.pop();
if(u.x==n && u.y==m) return u.d;
int ux = u.x,uy = u.y;
for(int i=0;i<4;i++){
int nx = ux+dir[i][0];
int ny = uy+dir[i][1];
int opt = e[ux][uy][nx][ny];
if(nx <1||ny<1||nx>n||ny>m||opt<0||(opt&&!(u.k&(1<<(opt-1))))) continue;
int nkey = u.k|getkey(nx,ny);
if(vis[nx][ny][nkey]) continue;
q.push(node(nx,ny,nkey,u.d+1));
vis[nx][ny][nkey] = 1;
}
}
return -1;
}
void read(){
int k,s,r;
scanf("%d%d%d",&n,&m,&r);
for(scanf("%d",&k);k--;){
int x1,y1,x2,y2,g;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&g);
if(g) e[x1][y1][x2][y2]=e[x2][y2][x1][y1]=g;
else e[x1][y1][x2][y2]=e[x2][y2][x1][y1]=-1;
}
for(scanf("%d",&s);s--;) {
int x,y,q;
scanf("%d%d%d",&x,&y,&q);
key[x][y][++dkey[x][y]]=q;
}
}
int main(){
read();
printf("%d\n",bfs());
return 0;
}
最短路版:
这里涉及到的变化还蛮多的,我们要先算出最多边数:1>>10*10*10=1024000;
把原来矩阵每个坐标存成一个点,再对可到邻边赋权值1,在建图的时候,我们只要连接一个双向边即可,假设j是从i左移一格,那么i就是j右移一格。上下同理。值得注意的是,我们每次在一层的时候,在没有钥匙i的时候,我们需要把i钥匙锁在的点u,连接到有了i钥匙的那一层对应的位置上v,权值是0,这样就建成了1>>种类 的图,并且图直接有连接。最后跑一遍最短路即可。
ps:要记录总层数的总点数,之后dis[]的初始化时所有点。
//分层求最短路
#include
using namespace std;
const int N = 12;
const int M = 1024100;
const int INF = 0x3f3f3f3f;
typedef pair P;
struct keyn{
int x,y;
}key[N][20];//key[i][j] 种类为i的钥匙第j把的坐标
int n,m,p,s,k,cnt,layer,nn,nsum;//n宽,m长,p种类,s总钥匙数,k总障碍数,cnt计数器,layer层数,nn每层的点,nsum总点数
int num[N][N],fg[200][200];
int head[M],nex[M],ver[M],edge[M];
int hadk[N],kn[N],vis[M],dis[M];
void add(int x,int y,int w){
ver[++cnt] = y;
nex[cnt] = head[x];
edge[cnt] = w;
head[x] = cnt;
}
void read(){
cnt = 0;
int x,y;
scanf("%d%d%d%d",&n,&m,&p,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
num[i][j] = ++cnt;
for(int i=1,g,u,v;i<=k;i++){
scanf("%d%d",&x,&y); u = num[x][y];
scanf("%d%d",&x,&y); v = num[x][y];
scanf("%d",&g);
if(g==0) g=-1;
fg[u][v] = fg[v][u] = g;
}
scanf("%d",&s);
for(int i = 1,q;i <= s;i++){
scanf("%d%d%d",&x,&y,&q);
kn[q]++;
key[q][kn[q]].x = x;
key[q][kn[q]].y = y;
}
}
void build(){
layer = 1< q;
for(int i = 0;i <= nsum; i++) dis[i] = INF;
q.push(make_pair(0,1)),dis[1] = 0;
while(q.size()){
int u = q.top().second;q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u];i;i = nex[i]){
int v = ver[i],w=edge[i];
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
q.push(make_pair(-dis[v],v));
}
}
}
}
void spfa(){
queue q;
for(int i = 0;i <= nsum;i++) dis[i] = INF;
q.push(1),dis[1] = 0,vis[1] = 1;
while(q.size()){
int u = q.front(); q.pop();vis[u] = 0;
for(int i=head[u];i;i = nex[i]){
int v = ver[i],w=edge[i];
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
if(!vis[v]){
q.push(v),vis[v] = 1;
}
}
}
}
}
void solve(){
int ans = INF;
for(int i =0;i