JavaScriptで1つの爆弾で倒せる最大の敵の数を求めるアルゴリズム
問題の概要
2次元グリッドが与えられ、各セルは壁「W」、敵「E」、空きマス「0」(ゼロ)のいずれかです。この課題では、1つの爆弾を使って倒せる敵の最大数を返す関数を作成します。
爆弾は、設置した地点から同じ行と列に存在するすべての敵を倒します。ただし、壁は非常に強固で破壊できないため、爆発の効果は壁にぶつかった時点で止まります。
また、爆弾を設置できるのは空きマス「0」のみである点にも注意が必要です。
入力例と出力
たとえば、関数への入力が以下の場合を考えてみましょう。
const arr = [
['0', 'E', '0', '0'],
['E', '0', 'W', 'E'],
['0', 'E', '0', '0']
];
このとき、期待される出力は次の通りです。
const output = 3;
出力の解説
位置 [1, 1] に爆弾を設置すると、同じ行の左側にいる敵1体と、同じ列の上下にいる敵2体、合計3体の敵を倒すことができます。これがこのグリッドで達成できる最大値です。
効率的なアプローチ
各空きマスについて毎回行・列方向の敵を数え直す素朴な方法では、計算量が O(m²n²) になり非効率です。そこで、すでに数えた結果を再利用する考え方を取り入れます。
ポイントは次の2つです。
- 行方向: 行の先頭にいるか、直前のセルが壁「W」である場合に限り、その区間の敵の数を再カウントします。
- 列方向: 列ごとに配列 cols を用意し、列の先頭にいるか、真上のセルが壁である場合に限り再カウントします。
こうすることで各セルは一度ずつ処理するだけでよく、時間計算量は O(m × n) まで抑えられます。
コード実装
const arr = [
['0', 'E', '0', '0'],
['E', '0', 'W', 'E'],
['0', 'E', '0', '0']
];
const killEnemy = (arr = []) => {
let m = arr.length;
let n = m > 0 ? arr[0].length : 0;
let result = 0, rows = 0;
const cols = [];
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
// 行の先頭、または左隣が壁なら行内の敵を再カウント
if (j === 0 || arr[i][j-1] === 'W') {
rows = 0;
for (let k = j; k < n && arr[i][k] !== 'W'; ++k)
if (arr[i][k] === 'E')
rows += 1;
}
// 列の先頭、または真上が壁なら列内の敵を再カウント
if (i === 0 || arr[i-1][j] === 'W') {
cols[j] = 0;
for (let k = i; k < m && arr[k][j] !== 'W'; ++k)
if (arr[k][j] === 'E')
cols[j] += 1;
}
// 空きマスなら、行+列の敵数の合計で最大値を更新
if (arr[i][j] === '0' && rows + cols[j] > result)
result = rows + cols[j];
}
}
return result;
};
console.log(killEnemy(arr));
出力
コンソールには次のように表示されます。
3
まとめ
この問題は「Bomb Enemy」として知られる定番のアルゴリズム問題です。壁で区切られた区間ごとに行・列の敵数をキャッシュすることで、全マスを走査しても線形時間で答えを求められるのが大きなポイントです。必要な追加メモリも列数分の配列だけで済むため、空間計算量 O(n) と非常に効率的な実装になっています。
-
JavaScriptのPromise(プロミス)とは?非同期処理の基本と使い方を解説
JavaScriptのPromise(プロミス)は、作成された時点ではまだ値が確定していない非同期処理を扱うためのオブジェクトです。APIリクエストやファイルの読み込みなど、完了まで時間がかかる処理について、「いずれ結果を受け取る」という約束を表現します。Promiseは、以下の3つの状態を持ちます。pending(保留中):処理がまだ完了しておらず、成功も失敗も確定していない初期状態fulfilled(履行済み):処理が正常に完了し、結果の値が利用できる状態rejected(拒否済み):処理が失敗し、エラーの理由が利用できる状態一度fulfilledまたはrejectedに遷移したPromi
-
JavaScriptのWeakSetとは?特徴と主要メソッド、サンプルコードをわかりやすく解説
JavaScriptのWeakSet(ウィークセット)は、オブジェクトを格納するためのコレクションです。Setと同様に、同じオブジェクトを重複して保存することはできません。WeakSetの主な特徴弱い参照で保持する:WeakSet内のオブジェクトへの参照が他に存在しなくなると、ガベージコレクションによって自動的にメモリから解放されます。そのため、メモリリークを防ぎたい場面で役立ちます。オブジェクトのみ格納可能:数値や文字列などのプリミティブ値は追加できません。列挙できない:Setのようなsizeプロパティや反復処理の仕組みを持たず、格納されている要素の一覧を取得することはできません。WeakS