今回は paiza 「【全探索 3】+1, -1, ‘1’+, +’1’」の問題に挑戦!
全探索ってことでシンプルに全部書いたなら長くなりすぎた(´;ω;`)
問題概要
◯ 入力
-
N個の自然数X1,X2,…,XNが与えられる。
◯ 操作ルール
- 任意の2つの数を選ぶ。
- それぞれの数に対して「次の操作のいずれかを1回だけ行える(行わなくても良い)」:
- +1 する
- -1 する
- 先頭に「1」をつける
- 末尾に「1」をつける
- これで得られた2つの数の差の絶対値を計算。
◯ 出力
- 操作後の 2 つの数の差の絶対値の "最小値" を出力。
入力例:
3
111
222
333
出力例:
109
✅ OK例:シンプルに全部書く
const rl = require('readline').createInterface({ input:process.stdin });
const lines = [];
rl.on('line', (input) => lines.push(input));
rl.on('close', () => {
const N = Number(lines[0]);
const X = lines.slice(1).map(Number);
let minDiff = Infinity;
for (let i = 0; i < N; i++) {
const A = X[i];
const A1 = A + 1;
const A2 = A - 1;
const A3 = '1' + A
const A4 = A + '1';
for (let j = 0; j < N; j++) {
if (i === j) continue;
const B = X[j];
const B1 = B + 1;
const B2 = B - 1;
const B3 = '1' + B;
const B4 = B + '1';
if (Math.abs(A - B) < minDiff) minDiff = Math.abs(A - B);
if (Math.abs(A - B1) < minDiff) minDiff = Math.abs(A - B1);
if (Math.abs(A - B2) < minDiff) minDiff = Math.abs(A - B2);
if (Math.abs(A - B3) < minDiff) minDiff = Math.abs(A - B3);
if (Math.abs(A - B4) < minDiff) minDiff = Math.abs(A - B4);
if (Math.abs(A1 - B) < minDiff) minDiff = Math.abs(A1 - B);
if (Math.abs(A1 - B1) < minDiff) minDiff = Math.abs(A1 - B1);
if (Math.abs(A1 - B2) < minDiff) minDiff = Math.abs(A1 - B2);
if (Math.abs(A1 - B3) < minDiff) minDiff = Math.abs(A1 - B3);
if (Math.abs(A1 - B4) < minDiff) minDiff = Math.abs(A1 - B4);
if (Math.abs(A2- B) < minDiff) minDiff = Math.abs(A2 - B);
if (Math.abs(A2 - B1) < minDiff) minDiff = Math.abs(A2 - B1);
if (Math.abs(A2 - B2) < minDiff) minDiff = Math.abs(A2 - B2);
if (Math.abs(A2 - B3) < minDiff) minDiff = Math.abs(A2 - B3);
if (Math.abs(A2 - B4) < minDiff) minDiff = Math.abs(A2 - B4);
if (Math.abs(A3 - B) < minDiff) minDiff = Math.abs(A3 - B);
if (Math.abs(A3 - B1) < minDiff) minDiff = Math.abs(A3 - B1);
if (Math.abs(A3 - B2) < minDiff) minDiff = Math.abs(A3 - B2);
if (Math.abs(A3 - B3) < minDiff) minDiff = Math.abs(A3 - B3);
if (Math.abs(A3 - B4) < minDiff) minDiff = Math.abs(A3 - B4);
if (Math.abs(A4 - B) < minDiff) minDiff = Math.abs(A4 - B);
if (Math.abs(A4 - B1) < minDiff) minDiff = Math.abs(A4 - B1);
if (Math.abs(A4 - B2) < minDiff) minDiff = Math.abs(A4 - B2);
if (Math.abs(A4 - B3) < minDiff) minDiff = Math.abs(A4 - B3);
if (Math.abs(A4 - B4) < minDiff) minDiff = Math.abs(A4 - B4);
}
}
console.log(minDiff);
});
◯ コードの流れ
- 入力の読み込み
-
Nを読み込み、続くXの配列を作る。
-
- 最小差を管理する変数
minDiffを初期化- 初期値は
Infinityにしておく。
- 初期値は
- 2重ループで2つの数字を選ぶ
- 外側ループで数字
Aを選び、内側ループで数字Bを選ぶ(ただしi === jの場合はスキップ)。
- 外側ループで数字
-
A,Bそれぞれの操作後の値を4つずつ生成-
A:A+1,A-1,'1'+A,A+'1' -
B:B+1,B-1,'1'+B,B+'1'
-
- 差の絶対値をひたすらチェック
- 25パターン (5×5) の組み合わせをすべて
ifで書き出している。 - その都度
minDiffを更新。
- 25パターン (5×5) の組み合わせをすべて
- 結果を出力
- 最終的に一番小さかった差を出力する。
◯ 特徴
- 動きは単純でわかりやすい。
- ただし if 文が25個も並んでいるため、冗長で「ゴリ押し」感が強い。
- 小規模問題だから成立するが、スッキリはしていない。
✨ OK例: 二次元配列 + 4重ループ(全探索)
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', (line) => lines.push(line));
rl.on('close', () => {
const N = Number(lines[0]);
const X = Array.from({ length: N }, () => Array(5).fill(0));
// 各数の5パターンを作成
for (let i = 0; i < N; i++) {
const val = Number(lines[i + 1]);
X[i][0] = val;
X[i][1] = val + 1;
X[i][2] = val - 1;
X[i][3] = Number("1" + val.toString());
X[i][4] = Number(val.toString() + "1");
}
let ans = Infinity;
// すべての組み合わせを調べる
for (let i = 0; i < N; i++) {
for (let j = i + 1; j < N; j++) {
for (let k = 0; k < 5; k++) {
for (let l = 0; l < 5; l++) {
ans = Math.min(ans, Math.abs(X[i][k] - X[j][l]));
}
}
}
}
console.log(ans);
});
◯ コードの流れ
- 入力の読み込み
-
Nを読み込み、長さ N × 5 の二次元配列 X を用意。
-
- 候補値を格納する
- 各数について 5パターンを
X[i]に入れる:- 0番目: 元の値
- 1番目: +1
- 2番目: -1
- 3番目: 先頭に1をつける
- 4番目: 末尾に1をつける
- 各数について 5パターンを
- 最小差を管理する変数
ansを初期化- 初期値は
Infinity。
- 初期値は
- 4重ループで全組み合わせを調べる
- 外側2重ループ: 2つの異なる数字のペア (
i,j) を選ぶ。 - 内側2重ループ: それぞれの候補値 (
k,l) を選ぶ。 -
Math.abs(X[i][k] - X[j][l])を計算し、ansを更新。
- 外側2重ループ: 2つの異なる数字のペア (
- 結果を出力
- 最終的に一番小さい差を出力する。
◯ 特徴
- 25パターンの if を書かず、ループでまとめて処理。
- 二次元配列に候補を保存しておくため、操作がシンプル。
- スッキリしていて、拡張性もある。
🗒️ まとめ
◯ 問題
- 「2つの数を選び、操作後の差の絶対値を最小化する」→ 全探索で解ける問題。
◯ 実装方針
- 各数について「元の数+4種類の操作結果」を列挙 → 計5パターンを作る。
- すべての組み合わせ(2つの数 → 5パターン × 5パターン)を比較し、最小差を求める。
◯ 解法の2パターン
- シンプル版
- 25通りの差を if 文で直接比較。
- 初学者でも動作が追いやすい。
- ただし冗長で保守性が低い。
- 二次元配列+4重ループ版
- 各数の候補を二次元配列にまとめて、ループで網羅。
- コードがスッキリし、拡張性も高い。
◯ 学びのポイント
- 制約が小さいときは「全探索」でシンプルに解ける。
- 冗長な書き方(if を25回書く)と、整理された書き方(二次元配列+ループ)の両方を比較すると、
- コードの冗長さ・保守性・見通しの違い が理解できる。
- 数字の操作に「文字列変換」を使うテクニック("1"+val, val+"1" → Number(...))も重要。