概要
R * C のグリッド状のマップがある。
マップには一つの出口があり、主人公が一人いる。
その他のセルは木があって通れないか、何もないかである。
何もないセルには 0 から 9 人の敵がいる。
主人公及び敵は一度の移動で隣接するセルに移動することができる。
主人公は出口までの移動手順を宣言してからその通りにゴールへと向かう。
敵と同じセルに止まった場合はその敵全てと戦わなければならない。
敵は主人公と戦うために最適な行動をする。
出口に辿り着くまでに戦う敵の数を最小化した場合の戦う回数を求めよ。
解法
敵の最適な行動は出口で待ち伏せすることです。
なので、ゴールへの距離が、主人公からゴールへの距離以下の敵とは全て戦わなければなりません。
これらの敵はどうやっても回避することができません。
また、遠回りをすると他の敵にもゴールへ先回りされる可能性があるので、主人公の最適なルートは最短経路でゴールまで行くことです。
まとめると、ゴールまでの距離が主人公からゴールへの距離以下の敵の数の総和が答えになります。
これを求めるには各セルについてゴールまでの距離が分かればよいので、ゴールを始点とした一回の BFS で求められます。
コード
typedef vector<int> VI; typedef vector<VI> VVI; typedef vector<string> VS; typedef pair<int,int> PII; typedef vector<PII> VPII; #define REP( i, m, n ) for ( int i = (int)( m ); i < (int)( n ); ++i ) #define FOR( v, c ) for ( auto &v : c ) #define PB( n ) push_back( n ) #define MP( a, b ) make_pair( ( a ), ( b ) ) #define fst first #define snd second const int INF = INT_MAX / 2; const int dy[] = { 0, 1, 0, -1 }; const int dx[] = { 1, 0, -1, 0 }; int main() { cin.tie( 0 ); ios::sync_with_stdio( false ); int h, w; cin >> h >> w; VS board( h ); FOR( line, board ) { cin >> line; } const PII pos_goal = [&](){ REP( i, 0, h ) { REP( j, 0, w ) { if ( board[i][j] == 'E' ) { return MP( i, j ); } } } return PII(); }(); VVI dist( h, VI( w, INF ) ); dist[ pos_goal.fst ][ pos_goal.snd ] = 0; queue<PII> que; que.push( pos_goal ); int dist_own; VPII breeders; // ( dist, number ) while ( !que.empty() ) { const int y = que.front().fst; const int x = que.front().snd; que.pop(); if ( board[y][x] == 'S' ) { dist_own = dist[y][x]; } if ( isdigit( board[y][x] ) ) { breeders.PB( MP( dist[y][x], board[y][x] - '0' ) ); } REP( d, 0, 4 ) { const int ny = y + dy[d]; const int nx = x + dx[d]; if ( 0 <= ny && ny < h && 0 <= nx && nx < w && board[ ny ][ nx ] != 'T' && dist[y][x] + 1 < dist[ ny ][ nx ] ) { dist[ny][nx] = dist[y][x] + 1; que.push( MP( ny, nx ) ); } } } int res = 0; FOR( b, breeders ) { res += b.fst <= dist_own ? b.snd : 0; } cout << res << endl; return 0; }