// yukicoder(No.697 池の数はいくつか) // https://yukicoder.me/submissions/329835 // -> 上記を拡張してみた. #include using namespace std; #define FOR(i, a, b) for(int i = (a); i < (b); ++i) constexpr int MAX = 625; constexpr int dx[] = {-1, 0, 1, 0}, dy[] = {0, -1, 0, 1}; int H, W; char board[MAX]; int memo[MAX]; // distance を 変数名で使用すると, In file included from /usr/include/c++/4.8.2/bits/stl_algobase.h:66:0 ... // のような error となるので, 使わない. // int dist[MAX]; // 幅優先探索. // https://ja.wikipedia.org/wiki/幅優先探索 // 迷路を幅優先探索する. // ※bfsの動作確認用. // @param c: 探索地点の迷路の座標. // @param d: 池の番号. // @return: 特に無し. void bfs(int c, int d){ // 1. 終了条件設定. if(board[c] == '#') return; // 2. 空のキュー. queue q; // 3. 訪問済みフラグ設定. memo[c] = d; // 4. 探索地点 c をキュー q に追加. q.push(c); while(!q.empty()){ // 5. キューから取り出す. int v = q.front(); q.pop(); // 6. 取り出した要素を処理. // x: 列方向, y: 行方向 で考える. int nx, ny, n; int cx = v % W , cy = v / W; FOR(i, 0, 4){ nx = cx + dx[i]; ny = cy + dy[i]; n = nx + ny * W; // cout << "cx=" << cx << " nx=" << nx << " cy=" << cy << " ny=" << ny << " n=" << n << " c=" << c << endl; // 7. 訪問不可能なマス であれば, 処理をスキップ. if(n < 0 || nx < 0 || nx >= W || ny < 0 || ny >= H) continue; if(memo[n] == d) continue; // 8. 訪問可能 かつ 未訪問 のマス であれば, 訪問済みを設定. if(board[n] == '.' && memo[n] == 0) memo[n] = d, q.push(n); } } return; } // 幅優先探索. // https://ja.wikipedia.org/wiki/幅優先探索 // 迷路を幅優先探索する. // ※bfsの動作確認用. // @param c: 探索地点の迷路の座標. // @param s: スタート地点. // @param g: ゴール地点. // @param dist: 探索距離を保存する二次元配列. // @param sIndex: 探索距離を保存する二次元配列のインデックス. // @return: 特に無し. void getDistance(int c, int s, int g, int dist[][MAX], int sIndex){ // 1. 終了条件設定. if(dist[sIndex][c] >= 1) return; // 2. 空のキュー. queue q; // 3. 訪問済みフラグ設定. dist[sIndex][c] = 0; // 4. 探索地点 c をキュー q に追加. q.push(c); while(!q.empty()){ // 5. キューから取り出す. int v = q.front(); q.pop(); // 6. 取り出した要素を処理. // x: 列方向, y: 行方向 で考える. int nx, ny, n; int cx = v % W , cy = v / W; FOR(i, 0, 4){ nx = cx + dx[i]; ny = cy + dy[i]; n = nx + ny * W; // cout << "cx=" << cx << " nx=" << nx << " cy=" << cy << " ny=" << ny << " n=" << n << " c=" << c << endl; // 7. 訪問不可能なマス であれば, 処理をスキップ. if(n < 0 || nx < 0 || nx >= W || ny < 0 || ny >= H) continue; if(memo[n] == s || memo[n] == g) continue; // 8. 訪問可能 かつ 未訪問 のマス であれば, 最短距離を設定. if(memo[n] != s && memo[n] != g && dist[sIndex][n] == 0) dist[sIndex][n] = dist[sIndex][v] + 1, q.push(n); } } return; } int main() { // 1. 入力情報取得. cin >> W >> H; FOR(i, 0, H){ string s; cin >> s; FOR(j, 0, W) board[i * W + j] = s[j]; } // 2. 空洞の分割. int counter = 1; FOR(i, 0, H * W){ if(board[i] == '.' && memo[i] == 0){ bfs(i, counter); counter++; } } // ex. // [入力例] // ######### // #.####.## // #..###.## // #..###..# // ######### // ↓ // [memo] // 0 0 0 0 0 0 0 0 0 // 0 1 0 0 0 0 2 0 0 // 0 1 1 0 0 0 2 0 0 // 0 1 1 0 0 0 2 2 0 // 0 0 0 0 0 0 0 0 0 // FOR(i, 0, H){ // FOR(j, 0, W){ // cout << memo[i * W + j] << " "; // } // cout << endl; // } // 3. スタート地点の個数をカウント. int sCount = 0; FOR(i, 0, H * W) if(memo[i] == 1) sCount++; // 4. 空洞から別の空洞へ探索してみる. // ※スタート地点の個数分実施??? int dist[sCount][MAX]; FOR(i, 0, sCount) FOR(j, 0, MAX) dist[i][j] = 0; int startIndex = 0; FOR(i, 0, H * W){ if(memo[i] == 1){ getDistance(i, 1, 2, dist, startIndex); startIndex++; } } // ex. // [入力例] // ######### // #.####.## // #..###.## // #..###..# // ######### // ↓ // [dist] // index=0 // 2 1 2 3 4 5 6 7 8 // 1 0 1 2 3 4 0 8 9 // 2 0 0 3 4 5 0 9 10 // 3 0 0 4 5 6 0 0 11 // 4 5 6 5 6 7 8 9 10 // index=1 // 3 4 5 6 7 8 9 10 11 // 2 0 6 7 8 9 0 11 12 // 1 0 0 8 9 10 0 12 13 // 2 0 0 7 8 9 0 0 12 // 3 4 5 6 7 8 9 10 11 // index=2 // 4 3 2 3 4 5 6 7 8 // 5 0 1 2 3 4 0 8 9 // 6 0 0 1 2 3 0 9 10 // 7 0 0 2 3 4 0 0 9 // 6 5 4 3 4 5 6 7 8 // index=3 // 4 5 6 7 8 9 10 11 12 // 3 0 7 6 7 8 0 12 11 // 2 0 0 5 6 7 0 11 10 // 1 0 0 4 5 6 0 0 9 // 2 1 2 3 4 5 6 7 8 // index=4 // 7 6 5 4 5 6 7 8 9 // 6 0 4 3 4 5 0 9 10 // 5 0 0 2 3 4 0 10 9 // 4 0 0 1 2 3 0 0 8 // 3 2 1 2 3 4 5 6 7 // FOR(m, 0, sCount){ // cout << "index=" << m << endl; // FOR(i, 0, H){ // FOR(j, 0, W){ // cout << dist[m][i * W + j] << " "; // } // cout << endl; // } // } // 4. 最短距離を探索. // memo[i] == 2 となる各地点の上下左右(index を j と見て)について, // memo[j] != 2 であることを確認しながら, dist[index][j] の最小値を確認し, 更新していく. int ans = 1e8; FOR(i, 0, H){ FOR(j, 0, W){ // memo[i] == 2 (ゴール地点) を 設定. if(memo[i * W + j] == 2){ // 上下左右を確認. int nx, ny, n; FOR(k, 0, 4){ nx = j + dx[k]; ny = i + dy[k]; n = nx + ny * W; // 訪問不可能なマス であれば, 処理をスキップ. if(n < 0 || nx < 0 || nx >= W || ny < 0 || ny >= H) continue; // memo[n] != 2 (ゴール手前の地点) を 設定し, dist[index][n] の 最小値 を 確認. if(memo[n] != 2){ // ※ 最小値 は, 1以上 のはずなので, dist[l][n] > 0 に限定して, 最小値を更新. FOR(l, 0, sCount) if(dist[l][n] > 0) ans = min(ans, dist[l][n]); } } } } } // 5. 出力 ~ 後処理. cout << ans << endl; return 0; }