題目描述 Description 小 B 最近迷上了華容道,可是他總是要花很長的時間才能完成一次。於是,他想到用編程來完成華容道:給定一種局面,華容道是否根本就無法完成,如果能完成,最少需要多少時間。 小 B 玩的華容道與經典的華容道游戲略有不同,游戲規則是這樣的: 在一個 n*m 棋盤上有 n*m ...
題目描述 Description
小 B 最近迷上了華容道,可是他總是要花很長的時間才能完成一次。於是,他想到用編程來完成華容道:給定一種局面,華容道是否根本就無法完成,如果能完成,最少需要多少時間。
小 B 玩的華容道與經典的華容道游戲略有不同,游戲規則是這樣的:
- 在一個 n*m 棋盤上有 n*m 個格子,其中有且只有一個格子是空白的,其餘 n*m-1個格子上每個格子上有一個棋子,每個棋子的大小都是 1*1 的;
- 有些棋子是固定的,有些棋子則是可以移動的;
- 任何與空白的格子相鄰(有公共的邊)的格子上的棋子都可以移動到空白格子上。 游戲的目的是把某個指定位置可以活動的棋子移動到目標位置。
給定一個棋盤,游戲可以玩
q 次,當然,每次棋盤上固定的格子是不會變的,但是棋盤上空白的格子的初始位置、指定的可移動的棋子的初始位置和目標位置卻可能不同。第 i
次玩的時候,空白的格子在第 EX_i 行第 EY_i 列,指定的可移動棋子的初始位置為第 SX_i 行第 SY_i 列,目標位置為第 TX_i
行第 TY_i 列。
假設小 B 每秒鐘能進行一次移動棋子的操作,而其他操作的時間都可以忽略不計。請你告訴小 B 每一次游戲所需要的最少時間,或者告訴他不可能完成游戲。
輸入描述 Input Description
第一行有 3 個整數,每兩個整數之間用一個空格隔開,依次表示 n、m 和 q;
接下來的 n 行描述一個 n*m 的棋盤,每行有 m 個整數,每兩個整數之間用一個空格隔開,每個整數描述棋盤上一個格子的狀態,0 表示該格子上的棋子是固定的,1 表示該格子上的棋子可以移動或者該格子是空白的。
接下來的 q 行,每行包含 6 個整數依次是 EX_i、EY_i、SX_i、SY_i、TX_i、TY_i,每兩個整數之間用一個空格隔開,表示每次游戲空白格子的位置,指定棋子的初始位置和目標位置。
輸出描述 Output Description
輸出有 q 行,每行包含 1 個整數,表示每次游戲所需要的最少時間,如果某次游戲無法完成目標則輸出-1。
樣例輸入 Sample Input
3 4 2
0 1 1 1
0 1 1 0
0 1 0 0
3 2 1 2 2 2
1 2 2 2 3 2
樣例輸出 Sample Output
2
-1
數據範圍及提示 Data Size & Hint
【樣例說明】
棋盤上劃叉的格子是固定的,紅色格子是目標位置,圓圈表示棋子,其中綠色圓圈表示目標棋子。
第一次游戲,空白格子的初始位置是 (3, 2)(圖中空白所示),游戲的目標是將初始位置在(1, 2)上的棋子(圖中綠色圓圈所代表的棋子)移動到目標位置(2, 2)(圖中紅色的格子)上。
移動過程如下:
第二次游戲,空白格子的初始位置是(1, 2)(圖中空白所示),游戲的目標是將初始位置在(2, 2)上的棋子(圖中綠色圓圈所示)移動到目標位置 (3, 2)上。
要將指定塊移入目標位置,必須先將空白塊移入目標位置,空白塊要移動到目標位置,必然是從位置(2,2)上與當前圖中目標位置上的棋子交換位置,之後能與空白塊交換位置的只有當前圖中目標位置上的那個棋子,因此目標棋子永遠無法走到它的目標位置,游戲無法完成。
【數據範圍】
對於 30%的數據,1 ≤ n, m ≤ 10,q = 1;
對於 60%的數據,1 ≤ n, m ≤ 30,q ≤ 10;
對於 100%的數據,1 ≤ n, m ≤ 30,q ≤ 500。
——————————————————————————————————————————————————————————————————————————
思路一(雖然沒錯但是會TLE)
一看到題目,聯想到以前做過的八數位。於是可以想到把空白塊當作可以自由移動的單位進行BFS。由於想不到什麼優化措施,所以就敲了一段大爆搜……雖然TLE是意料之中,但是沒想到竟然能拿到70分(那這就說明我離正解不遠了/誤)
蒟蒻的大爆搜就別看了qwq——————————————————————————————————————————————————————————————————————————
思路二(正解)
整理一下為什麼思路一的BFS是錯的。
在一個規模較小的數據範圍內,思路一的BFS是可以在1s內得出結論的……但是很不幸,雖然出題人很善良的給了70分(是不是沒卡掉),但是爆搜還是沒有前途的;
思路一的BFS缺陷就是,白塊是以一種玄妙不可預測的路線行進的(誤)。在搜索的時候會浪費很多時間在根本不可能的情況上(不做無法實現的夢)。
所以在冥(ming)思(le)苦(ti)想(jie)以後想到了絕妙的演算法!
對於最優的操作,有一個前提是空白塊一定要先移動到欽點塊的旁邊。然後空白塊和欽點塊再作為一個整體移動。
空白塊和欽點塊的移動可以用SPFA來求最短路徑。建圖的方法就是把空白塊和欽點塊的位置的狀態看作一個點,再把每個狀態之間互相轉換的過程看作邊,在這個圖裡跑一邊SPFA就可以輕鬆求出正解!
以下是蒟蒻敲了一個下午的代碼
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<cstring> 5 using namespace std; 6 #define MAXN 31 7 #define MAXM 40001 8 #define INF 0x3f3f3f3f 9 int n,m,q,num[MAXN][MAXN][5],tot,cnt,head[MAXM],vis[MAXN][MAXN],ex,ey,sx,sy,tx,ty,dis[MAXM],used[MAXM],mp[MAXN][MAXN]; 10 const int dx[]={1,-1,0,0},dy[]={0,0,1,-1}; 11 struct Edge 12 { 13 int to,dis,next; 14 }e[MAXM]; 15 struct ZT{int x,y,steps;}; 16 void add(int from,int to,int dis) 17 { 18 e[++cnt].next=head[from]; 19 e[cnt].to=to; 20 e[cnt].dis=dis; 21 head[from]=cnt; 22 } 23 int bfs(int ax,int ay,int bx,int by,int cx,int cy) 24 { 25 if(ax==bx&&ay==by) return 0; 26 memset(vis,0,sizeof(vis)); 27 queue<ZT>Q; 28 while(!Q.empty()) Q.pop(); 29 Q.push((ZT){ax,ay,0}); 30 vis[ax][ay]=vis[cx][cy]=1; 31 while(!Q.empty()) 32 { 33 ZT u=Q.front();Q.pop(); 34 if(u.x==bx&&u.y==by) return u.steps; 35 for(int i=0;i<4;i++) 36 { 37 int x=u.x+dx[i],y=u.y+dy[i]; 38 if(x>=1&&x<=n&&y>=1&&y<=m&&mp[x][y]&&!vis[x][y]) 39 { 40 Q.push((ZT){x,y,u.steps+1}); 41 vis[x][y]=1; 42 if(x==bx&&y==by) return u.steps+1; 43 } 44 } 45 } 46 return INF; 47 } 48 void init() 49 { 50 for(int i=1;i<=n;i++) 51 for(int j=1;j<=m;j++) 52 for(int k=0;k<4;k++) 53 if(mp[i][j]&&mp[i+dx[k]][j+dy[k]]) 54 num[i][j][k]=++tot; 55 for(int i=1;i<=n;i++) 56 for(int j=1;j<=m;j++) 57 for(int k=0;k<4;k++) 58 if(num[i][j][k]) 59 add(num[i][j][k],num[i+dx[k]][j+dy[k]][k^1],1); 60 for(int i=1;i<=n;i++) 61 for(int j=1;j<=m;j++) 62 for(int k=0;k<4;k++) 63 for(int l=0;l<4;l++) 64 if(l!=k&&num[i][j][k]&&num[i][j][l]) 65 add(num[i][j][k],num[i][j][l],bfs(i+dx[k],j+dy[k],i+dx[l],j+dy[l],i,j)); 66 } 67 int spfa() 68 { 69 queue<int>Q; 70 if(sx==tx&&sy==ty) return 0; 71 for(int i=1;i<=tot;i++) dis[i]=INF; 72 for(int k=0;k<4;k++) 73 { 74 if(num[sx][sy][k]) 75 { 76 dis[num[sx][sy][k]]=bfs(ex,ey,sx+dx[k],sy+dy[k],sx,sy); 77 Q.push(num[sx][sy][k]); 78 } 79 } 80 while(!Q.empty()) 81 { 82 int u=Q.front();Q.pop(); 83 used[u]=0; 84 for(int i=head[u];i;i=e[i].next) 85 { 86 int v=e[i].to; 87 if(dis[v]>dis[u]+e[i].dis) 88 { 89 dis[v]=dis[u]+e[i].dis; 90 if(!used[v]) 91 { 92 Q.push(v); 93 used[v]=1; 94 } 95 } 96 } 97 } 98 int ans=INF; 99 for(int k=0;k<4;k++) 100 { 101 if(num[tx][ty][k]) ans=min(ans,dis[num[tx][ty][k]]); 102 } 103 return ans==INF?-1:ans; 104 } 105 int main() 106 { 107 scanf("%d%d%d",&n,&m,&q); 108 for(int i=1;i<=n;i++) 109 for(int j=1;j<=m;j++) 110 scanf("%d",&mp[i][j]); 111 init(); 112 while(q--) 113 { 114 scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty); 115 printf("%d\n",spfa()); 116 } 117 return 0; 118 }118行orz
這道題做的我要死了orz
順便吐槽一下為什麼NOIP2013D2前兩道都是普及的水題,到最後一道題就這麼難orz