这里一看就是找箱子到终点的最短路
一开始还傻傻的以为人的位置给的很没有意思- -,然后果然错了
没过多久想明白了错误,因为你推箱子并不是你想去哪里推就能去哪推的,首先得考虑人能否过的去,因为可能人被箱子或墙挡住都是可能的
虽然想明白了,但还是写了好久改了好久~~代码能力还是太渣了
利用dfs判定人能否走到所在的位置,bfs搜最短路
dp[i][j][4] 表示在第(i , j)位置上可以用4个方向推箱子,所以又得乘4种状态,最后就不断bfs计算这个dp值就好了
要细心加上耐心哦~~
1 /*我在这里假设箱子到了终点,我不断将它往回推,看能够推到的地方以及最短步数*/ 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <queue> 6 using namespace std; 7 int dir[4][2] = {{1 , 0} , {0 , 1} , {-1 , 0} , {0 , -1}}; 8 int m , n , dp[9][9][5] , a[9][9] , vis[9][9][5]; 9 int can[9][9]; //表示人能否到达那个位置 10 11 struct Node{ 12 int x , y , pos; //pos表示人的位置,0,1,2,3表示4个方向 13 Node(int x = 0 , int y = 0 , int pos = 0):x(x),y(y),pos(pos){} 14 }; 15 16 queue<Node> q; 17 //判断箱子从x , y按第 i 种方向推能否成立 18 bool ok(int x , int y , int i) 19 { 20 int x1 = x+dir[i][0]; 21 int y1 = y+dir[i][1]; 22 int x2 = x-dir[i][0]; 23 int y2 = y-dir[i][1]; 24 if(x1<1 || x1>m || y1<1 || y1>n || x2<1 || x2>m || y2<1 || y2>n) 25 return false; 26 if(a[x1][y1] == 1 || a[x2][y2] == 1) return false; 27 return true; 28 } 29 //用dfs搜索人所能走到的位置 30 void dfs(int x , int y) 31 { 32 can[x][y] = 1; 33 for(int i = 0 ; i<4 ; i++){ 34 int tx = x+dir[i][0]; 35 int ty = y+dir[i][1]; 36 if(tx>=1 && tx<=m && ty>=1 && ty<=n) 37 if(a[tx][ty]!=1 && !can[tx][ty]) dfs(tx , ty); 38 } 39 } 40 /*判断人从x1,y1 到x2 , y2这个位置在有箱子位于xbox , ybox遮挡的情况下能否到达*/ 41 bool canTo(int x1 , int y1 , int x2 , int y2 , int xbox , int ybox) 42 { 43 int tmp = a[xbox][ybox]; 44 a[xbox][ybox] = 1;//暂时将箱子所在位置变成墙 45 46 memset(can , 0 , sizeof(can)); 47 dfs(x1 , y1); 48 49 a[xbox][ybox] = tmp;//还原 50 if(can[x2][y2] == 1) return true; 51 return false; 52 } 53 54 void bfs(int sx , int sy , int pos) 55 { 56 dp[sx][sy][pos] = 0; 57 q.push(Node(sx , sy , pos)); 58 while(!q.empty()){ 59 Node u = q.front(); 60 q.pop(); 61 vis[u.x][u.y][u.pos] = 0; 62 for(int i = 0 ; i<4 ; i++){ 63 //人当前的位置 64 int curx = u.x + dir[u.pos][0]; 65 int cury = u.y + dir[u.pos][1]; 66 //人将要走到的位置 67 int nextx = u.x - dir[i][0]; 68 int nexty = u.y - dir[i][1]; 69 //先判定人即将到达的方向是否合理 70 if(nextx < 1 || nextx > m || nexty < 1 || nexty>n) continue; 71 //再判定人能否到达推箱子所在的位置 72 if(!canTo(curx , cury , nextx , nexty , u.x , u.y)) continue; 73 74 //如果箱子可以移动,那就记录移动时人所在的方向p 75 int p = (i+2) % 4; //人是在箱子的反方向,这里可以这样用是因为dir中的方向设置的对称 76 Node v = Node(u.x+dir[i][0] , u.y+dir[i][1] , p); 77 78 if(ok(u.x , u.y , i)){ 79 // cout<<"pos: "<<v.x<<" "<<v.y<<" "<<dp[v.x][v.y]<<endl; 80 if(dp[v.x][v.y][v.pos] > dp[u.x][u.y][u.pos] + 1){ 81 dp[v.x][v.y][v.pos] = dp[u.x][u.y][u.pos] + 1; 82 if(!vis[v.x][v.y][v.pos]){ 83 vis[v.x][v.y][v.pos] = 1; 84 q.push(v); 85 } 86 } 87 } 88 } 89 } 90 } 91 92 int main() 93 { 94 // freopen("a.in" , "r" , stdin); 95 int T; 96 scanf("%d" , &T); 97 while (T--){ 98 scanf("%d%d" , &m , &n); 99 Node tmp1 , tmp2 , tmp3; //用来记录终点的位置,和箱子的位置和人的位置 100 for(int i = 1 ; i<=m ; i++) 101 for(int j = 1 ; j<=n ; j++){ 102 scanf("%d" , &a[i][j]); 103 if(a[i][j] == 3) tmp1 = Node(i , j , 0); 104 if(a[i][j] == 2) tmp2 = Node(i , j , 0); 105 if(a[i][j] == 4) tmp3 = Node(i , j , 0); 106 } 107 108 memset(dp , 0x3f , sizeof(dp)); 109 memset(vis , 0 , sizeof(vis)); 110 111 memset(can , 0 , sizeof(can)); 112 a[tmp2.x][tmp2.y] = 1; 113 dfs(tmp3.x , tmp3.y); 114 a[tmp2.x][tmp2.y] = 2; 115 //看最开始人能从哪个位置开始推 116 for(int i = 0 ; i<4 ; i++){ 117 int tx = tmp2.x + dir[i][0]; 118 int ty = tmp2.y + dir[i][1]; 119 if(tx >= 1 && tx <= m && ty >= 1 && ty <= n) 120 if(can[tx][ty]){ 121 // cout<<"haha: "<<tx<<" " <<ty <<" "<<i<<endl; 122 bfs(tmp2.x , tmp2.y , i); 123 } 124 } 125 126 int minn = 0x3f3f3f3f; 127 for(int i = 0 ; i<4 ; i++){ 128 minn = min(minn , dp[tmp1.x][tmp1.y][i]); 129 } 130 if(minn < 0x3f3f3f3f) printf("%d " , minn); 131 else printf("-1 "); 132 } 133 return 0; 134 }