题目链接:hdu 1253,题目大意:从一个立方体的左下角走到右上角(两点间的连线为长方体的体对角线)的最少时间(步数),走不到或者最少时间达不到要求输出 -1,否则输出该时间。
因为要求"最少"之类的,所以很容易想到广搜,没错,这是 bfs 的裸题,只是它的状态数是个三维数组,转移的状态(方向)也从二维数组的4个变成了6个而已,但原理是一样的,直接广搜即可,只是这题的坑未免也太不人性化了,我 wa了几发看了讨论区后才知道原来起点不管是不是墙都不用管(继续搜),只需看终点即可。
首先是第一个版本的代码,用个三维的 dp 数组来记录从起点出发到当前结点的时间/步数,比较清晰:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<queue> 5 using namespace std; 6 #define For(i,s,t) for(int i=s; i<t; ++i) 7 const int inf= 0x2fffffff; 8 int a,b,c,T; 9 int f[60][60][60], dp[60][60][60]; 10 11 int dir[8][3]= {{0,0,1},{0,1,0},{-1,0,0},{1,0,0},{0,-1,0},{0,0,-1}}; 12 13 struct node{ 14 int x,y,z; 15 node(int x=0, int y=0, int z=0): x(x),y(y),z(z) {} 16 }; 17 18 void solve(){ 19 scanf("%d%d%d%d",&a,&b,&c,&T); 20 For(i,0,a+2) For(j,0,b+2) For(k,0,c+2) { 21 f[i][j][k]= 1; dp[i][j][k]= inf; 22 } 23 For(i,1,a+1) For(j,1,b+1) For(k,1,c+1) 24 scanf("%d",f[i][j]+k); 25 26 //如果终点是墙就永远出不了,(这题挺坑的,起点不管是不是墙都不要紧) 27 //或者从起点到终点的最短直线距离比 T大的话也出不了,是个很重要的剪枝 28 if(f[a][b][c]==1 || a+b+c-3 > T) { puts("-1"); return ; } 29 30 queue<node> q; 31 q.push(node(1,1,1)); 32 dp[1][1][1]= 0; 33 while(q.size()){ 34 node p= q.front(); q.pop(); 35 for(int k=0; k<6; ++k){ 36 int dx= p.x+dir[k][0], dy= p.y+dir[k][1], dz= p.z+dir[k][2]; 37 if(f[dx][dy][dz]!= 1 && dp[dx][dy][dz]== inf){ 38 dp[dx][dy][dz]= dp[p.x][p.y][p.z]+1; 39 q.push(node(dx,dy,dz)); 40 } 41 } 42 } 43 //算出到达终点的时间后,还需判断是否满足题意 44 if(dp[a][b][c] > T) puts("-1"); 45 else printf("%d\n",dp[a][b][c]); 46 } 47 48 int main(){ 49 int k; 50 scanf("%d",&k); 51 while(k--) solve(); 52 return 0; 53 }
然后第二个版本的,省去了dp数组,改用结点的结构体里增多一个变量来记录时间:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<queue> 5 #include<algorithm> 6 using namespace std; 7 #define For(i,s,t) for(int i=s; i<t; ++i) 8 const int inf= 0x2fffffff; 9 int a,b,c,T; 10 int f[60][60][60]; 11 12 int dir[8][3]= {{0,0,1},{0,1,0},{-1,0,0},{1,0,0},{0,-1,0},{0,0,-1}}; 13 14 struct node{ 15 int x,y,z,t; 16 node(int x=0, int y=0, int z=0, int t= inf): x(x),y(y),z(z),t(t) {} 17 bool operator ==(const node &n2) const { 18 return x==n2.x && y==n2.y && z==n2.z; 19 } 20 }; 21 22 void solve(){ 23 scanf("%d%d%d%d",&a,&b,&c,&T); 24 For(i,0,a+2) For(j,0,b+2) For(k,0,c+2) 25 f[i][j][k]= 1; 26 For(i,1,a+1) For(j,1,b+1) For(k,1,c+1) 27 scanf("%d",f[i][j]+k); 28 29 //如果终点是墙就永远出不了,(这题挺坑的,起点不管是不是墙都不要紧) 30 //或者从起点到终点的最短直线距离比 T大的话也出不了,是个很重要的剪枝 31 if(f[a][b][c]==1 || a+b+c-3 > T) { puts("-1"); return ; } 32 33 node ed(a,b,c); 34 queue<node> q; 35 q.push(node(1,1,1,0)); 36 while(q.size()){ 37 node p= q.front(); q.pop(); 38 if(p == ed) ed.t= min(ed.t,p.t); 39 for(int k=0; k<6; ++k){ 40 int dx= p.x+dir[k][0], dy= p.y+dir[k][1], dz= p.z+dir[k][2]; 41 if(f[dx][dy][dz]!= 1){ 42 q.push(node(dx,dy,dz,p.t+1)); 43 f[dx][dy][dz]= 1; 44 } 45 } 46 } 47 //算出到达终点的时间后,还需判断是否满足题意 48 if(ed.t > T) puts("-1"); 49 else printf("%d\n",ed.t); 50 } 51 52 int main(){ 53 int k; 54 scanf("%d",&k); 55 while(k--) solve(); 56 return 0; 57 }
方向数组 dir[][][] 果然还是把 x,y,z 封装在一维里更好一些~~