原题见洛谷。
分析
这题其实可以分层建图,然后跑SFPA,但单纯BFS代码量明显小得多。
先看数据范围:最大为10,所以空间可以随便搞。
开一个mp[11][11][11][11],表示(x1,y1),(x2,y2)之间是否有什么东西,然后开一个key[11][11][16]存(x,y)放在的钥匙编号,开一个cnt[11][11]记录(x,y)钥匙数量,开一个vis[11][11][2100]记录是否访问到。
这题特点(坑点)有以下几点:
1,一类钥匙一把可以重复使用,所以状压还是很方便。
2,输入mp和BFS时容易忽略两点间什么都没有的情况。
3,开vis数组时可以大胆点开大一些。
4,输出-1。
#include<bits/stdc++.h>
using namespace std;
int N,M,P,K,S,vis[11][11][2100],mp[11][11][11][11],key[11][11][16],cnt[11][11],dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
struct data{int x,y,step,state;}; queue<data>q;
char c;
void scan(int &x)
{
for(c=getchar();c<'0'||c>'9';c=getchar());
for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
}
int BFS()
{
data t;
int now=0,now_x,now_y,now_state,now_step,t_x,t_y,t_state,k;
for(int i=1;i<=cnt[1][1];i++) now=now|(1<<(key[1][1][i]));
q.push((data){1,1,0,now}); vis[1][1][now]=1;
while(!q.empty())
{
t=q.front(); q.pop();
now_x=t.x,now_y=t.y,now_state=t.state,now_step=t.step; //便于操作
for(int i=0;i<=3;i++)
{
t_x=t.x+dx[i],t_y=t.y+dy[i];
if(t_x<1||t_x>N||t_y<1||t_y>M) continue; //越界
k=mp[now_x][now_y][t_x][t_y];
if(k==-1) continue; //有墙
if(k&&!(now_state&(1<<k))) continue; //有锁,没钥匙
if(t_x==N&&t_y==M) return now_step+1; //这个格子可以到了,先看是不是终点
t_state=now_state; //不是终点,先把这个点的钥匙拿走
for(int j=1;j<=cnt[t_x][t_y];j++) t_state=t_state|(1<<(key[t_x][t_y][j]));
if(vis[t_x][t_y][t_state]) continue; //访问过
vis[t_x][t_y][t_state]=1;
q.push((data){t_x,t_y,now_step+1,t_state});
}
}
return -1;
}
int main()
{
int i,x1,x2,y1,y2,op;
scan(N);scan(M);scan(P);scan(K);
for(i=1;i<=K;i++)
{
scan(x1);scan(y1);scan(x2);scan(y2);scan(op);
mp[x1][y1][x2][y2]=mp[x2][y2][x1][y1]=op?op:-1;
} scan(S);
for(i=1;i<=S;i++)
{
scan(x1);scan(y1);scan(op);
cnt[x1][y1]++; key[x1][y1][cnt[x1][y1]]=op;
} cout<<BFS(); return 0;
}