/* 这题实在调糊了 借鉴的题解的一些判断方法 位运算大法好 - - 因为要集齐所有的宝石所以状态压缩一下 f[i][j][s]将s化为二进制 每一0表示该宝石没有 1表示该宝石有 有:到(i,j)这个点时 宝石收集状况为s的状态是否存在 */ #include#include #include #include using namespace std; int T,n,m,k,f[210][210][33],sx,sy,ex,ey,num; int xx[5]={0,0,0,1,-1}; int yy[5]={0,1,-1,0,0}; char s[210][210]; struct point { int mx, my, step, sum; point (int xx, int yy, int tt, int ss):mx(xx), my(yy), step(tt), sum(ss){}; }; struct door { int xi, yi; }door[15]; bool check(int s) { int cnt=0; for(int i=0;i<=4;i++) if((s>>i)&1==1) cnt++; return(cnt>=k); } int main() { scanf("%d",&T); while(T--) { memset(f,0,sizeof(f)); memset(s,0,sizeof(s)); num=0; queue q; int falg=0; scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { cin>>s[i][j]; if(s[i][j]=='S') { s[i][j]='.'; sx=i; sy=j; } if(s[i][j]=='E') { s[i][j]='.'; ex=i; ey=j; } if(s[i][j]=='$') { num++; door[num].xi=i; door[num].yi=j; } } q.push(point(sx,sy,0,0)); f[sx][sy][0]=1;//初始状态存在 while(!q.empty()) { point tmmp=q.front(); q.pop(); int nx=tmmp.mx; int ny=tmmp.my; int su=tmmp.sum; int time=tmmp.step; if(nx==ex&&ny==ey&&check(su))//判断是否符合条件了 { printf("%d\n",tmmp.step); falg=1;//多组数据害死人啊 一开始return 0 了 } if(falg)break; if(s[nx][ny]=='.')//一般的情况 直接周围的点入队 for(int i=1;i<=4;i++) { int ox=nx+xx[i]; int oy=ny+yy[i]; if(ox>0&&ox<=n&&oy>0&&oy<=m&&f[ox][oy][su]==0&&s[ox][oy]!='#') { f[ox][oy][su]=1; q.push(point(ox,oy,time+1,su)); } } if(s[nx][ny]=='$')//传送门 把所有传送门周围的点都入队 { for(int i=1;i<=num;i++) for(int j=1;j<=4;j++) { int ox=door[i].xi+xx[j]; int oy=door[i].yi+yy[j]; if(ox>0&&ox<=n&&oy>0&&oy<=m&&f[ox][oy][su]==0&&s[ox][oy]!='#') { f[ox][oy][su]=1; q.push(point(ox,oy,time+1,su)); } } } if(s[nx][ny]>='0'&&s[nx][ny]<='4')//开始收集宝石了 { int si=su|(1<< (s[nx][ny]-'0'));//转化出新状态 for(int i=1;i<=4;i++) { int ox=nx+xx[i]; int oy=ny+yy[i]; if(ox>0&&ox<=n&&oy>0&&oy<=m&&f[ox][oy][si]==0&&s[ox][oy]!='#') { f[ox][oy][si]=1; q.push(point(ox,oy,time+1,si)); } } } } if(!falg) printf("oop!\n"); } return 0; }