Problem:
チーズ (Cheese)

問題

今年も JOI 町のチーズ工場がチーズの生産を始め，ねずみが巣から顔を出した．JOI 町は東西南北に区画整理されていて，各区画は巣，チーズ工場，障害物，空き地のいずれかである．ねずみは巣から出発して全てのチーズ工場を訪れチーズを 1 個ずつ食べる．

この町には，N 個のチーズ工場があり，どの工場も１種類のチーズだけを生産している．チーズの硬さは工場によって異なっており，硬さ 1 から N までのチーズを生産するチーズ工場がちょうど 1 つずつある．

ねずみの最初の体力は 1 であり，チーズを 1 個食べるごとに体力が 1 増える．ただし，ねずみは自分の体力よりも硬いチーズを食べることはできない．

ねずみは，東西南北に隣り合う区画に 1 分で移動することができるが，障害物の区画には入ることができない．チーズ工場をチーズを食べずに通り過ぎることもできる．すべてのチーズを食べ終えるまでにかかる最短時間を求めるプログラムを書け．ただし，ねずみがチーズを食べるのにかかる時間は無視できる．

入力

入力は H+1 行ある．1 行目には 3 つの整数 H，W，N (1 ≤ H ≤ 1000，1 ≤ W ≤ 1000，1 ≤ N ≤ 9) がこの順に空白で区切られて書かれている．2 行目から H+1 行目までの各行には，'S'，'1', '2', ..., '9'，'X'，'.' からなる W 文字の文字列が書かれており，各々が各区画の状態を表している．北から i 番目，西から j 番目の区画を (i,j) と記述することにすると (1 ≤ i ≤ H, 1 ≤ j ≤ W)，第 i+1 行目の j 番目の文字は，区画 (i,j) が巣である場合は 'S' となり，障害物である場合は 'X' となり，空き地である場合は '.' となり，硬さ 1, 2, ..., 9 のチーズを生産する工場である場合はそれぞれ '1', '2', ..., '9' となる．入力には巣と硬さ 1, 2, ..., N のチーズを生産する工場がそれぞれ 1 つずつある．他のマスは障害物または空き地であることが保証されている．ねずみは全てのチーズを食べられることが保証されている．

出力

すべてのチーズを食べ終えるまでにかかる最短時間（分）を表す整数を 1 行で出力せよ．

入出力例

入力例 1

3 3 1
S..
...
..1

出力例 1

4

入力例 2

4 5 2
.X..1
....X
.XX.S
.2.X.

出力例 2

12

入力例 3

10 10 9
.X...X.S.X
6..5X..X1X
...XXXX..X
X..9X...X.
8.X2X..X3X
...XX.X4..
XX....7X..
X..X..XX..
X...X.XX..
..X.......

出力例 3

91

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;

namespace _0558
{
    class Program
    {
        static bool[,] canwalk;
        static int[] movex = new int[] { 1, -1, 0, 0 };
        static int[] movey = new int[] { 0, 0, 1, -1 };
        static int[,] d;
        const int inf = 100000000;
        static int bfs(int startx, int starty, int goalx, int goaly)
        {
            Queue<int> xqueue = new Queue<int>();
            Queue<int> yqueue = new Queue<int>();
            xqueue.Enqueue(startx); yqueue.Enqueue(starty);
            d[startx, starty] = 0;
            while (true)
            {
                int nowx = xqueue.Dequeue(); int nowy = yqueue.Dequeue();
                if (nowx == goalx && nowy == goaly) break;
                for (int i = 0; i < 4; i++)
                {
                    if (canwalk[nowx + movex[i], nowy + movey[i]] &&
                        d[nowx + movex[i], nowy + movey[i]] == inf)
                    {
                        d[nowx + movex[i], nowy + movey[i]] = d[nowx, nowy] + 1;
                        xqueue.Enqueue(nowx + movex[i]); yqueue.Enqueue(nowy + movey[i]);
                    }
                }
            }
            return d[goalx, goaly];
        }
        static void Main(string[] args)
        {
            int[] x = Console.ReadLine().Split().Select(int.Parse).ToArray();
            canwalk = new bool[x[0] + 2, x[1] + 2]; d = new int[x[0] + 2, x[1] + 2];
            int[,] cheese = new int[x[2] + 1, 2];
            for (int i = 0; i < x[0]; i++)
            {
                string s = Console.ReadLine();
                for (int j = 0; j < x[1]; j++)
                {
                    if (s[j] != 'X') canwalk[i + 1, j + 1] = true;
                    switch (s[j])
                    {
                        case ('X'): break;
                        case ('.'): break;
                        case ('S'):
                        {
                            cheese[0, 0] = i + 1; cheese[0, 1] = j + 1;
                            break;
                        }
                        default:
                        {
                            int n = int.Parse(s[j].ToString());
                            cheese[n, 0] = i + 1; cheese[n, 1] = j + 1;
                            break;
                        }
                    }
                }
            }
            int ret = 0;
            for (int i = 0; i < x[2]; i++)
            {
                for (int j = 0; j < x[0] + 2; j++)
                    for (int k = 0; k < x[1] + 2; k++)
                        d[j, k] = inf;
                ret += bfs(cheese[i, 0], cheese[i, 1], cheese[i + 1, 0], cheese[i + 1, 1]);
            }
            Console.WriteLine(ret);
        }
    }
}