今回は paiza の「名刺バインダー管理」の問題に挑戦!
問題概要
状況
- 名刺は「ファイル」に収められている。
- 1ファイルには
n個のポケット が横に並んでいて、1ポケットに「表と裏」の2枚の名刺が入っている。 - したがって 1ファイルに 2n枚の名刺 がある。
並び方
- 表:1〜n 番が左から順に並ぶ
- 裏:n+1〜2n 番が左から順に並ぶ
- ただし裏は背中合わせなので、「表の左端 ↔ 裏の右端」がペアになる。
与えられるもの
-
n(ポケット数) -
m(調べたい名刺番号)
求めるもの
- 「
m番の名刺の裏側の番号」
入力例:
3 1 // n m
出力例:
6
✅OK例:
const rl = require('readline').createInterface({ input:process.stdin });
const lines = [];
rl.on('line', (input) => lines.push(input));
rl.on('close', () => {
const [n, m] = lines[0].split(' ').map(Number);
const file = Math.floor((m - 1) / (2 * n));
const pos = (m - 1) % (2 * n);
const pairPos = 2 * n - 1 - pos;
const result = file * 2 * n + pairPos + 1;
console.log(result);
});
全体のアイデア
-
1つのファイルには 表
n枚 + 裏n枚 =2n枚 が入っている。 -
mがどのファイル(0始まり)にあるかを求め、そのファイル内での位置を見れば、同じポケットの裏側の位置がすぐに求まる。 -
実装は「0始まりに直して考える」のが楽 →
m-1を使う。
図解で理解する「バインダーの構造」
例:n = 3 の場合
→ 1ファイルには「表 3 枚 + 裏 3 枚 = 6 枚」が入っている
表から見た場合(左→右)
+---+---+---+
| 1| 2| 3|
+---+---+---+
裏から見た場合(左→右)
+---+---+---+
| 4| 5| 6|
+---+---+---+
でも実際は「ポケット」が3つあり、1つのポケットに2枚(表と裏)が背中合わせで入っている。
- ポケット 1: 表=1, 裏=6
- ポケット 2: 表=2, 裏=5
- ポケット 3: 表=3, 裏=4
🔍 コードの詳しい説明
① どのファイルか?
const file = Math.floor((m - 1) / (2 * n));
-
m-1で 0始まり に変換(例:m=1→0)。 -
2*nごとにファイルが区切られているので、商を取れば 何番目のファイルか(0始まり)になる。
(1ファイルに 2n 枚入っているので、(m-1)/(2*n)の商でファイル番号が決まる)
例:n=3 のとき、2*n = 6。
-
m = 1 ~ 6はfile = 0(1枚目のファイル) -
m = 7 ~ 12はfile = 1(2枚目のファイル)
② ファイル内の位置
const pos = (m - 1) % (2 * n);
- ファイル内での 相対位置(0〜2n-1) を求める。
- そのファイルの中で左から何番目か(0始まり)
-
0がそのファイルの先頭(左端の表)、2n-1がそのファイルの最後(右端の裏)。
例(n=3):
-
m=1→pos=0 -
m=3→pos=2 -
m=4→pos=3 -
m=6→pos=5
n=3 なら pos の範囲は 0〜5。
図で書くとこう:
pos: 0 1 2 3 4 5 (→ 0 ~ 2n-1)
表: 1 2 3
裏: 4 5 6
③ 裏側の位置
const pairPos = 2 * n - 1 - pos;
-
同じポケットの裏側はファイル内で鏡像の位置にある、という性質を使っている。
-
ファイル内のインデックスを
0 ~ (2n-1)とすると、「ペアはposと2n-1 – pos」 の組。
-
直感的な考え方:左から
i番目(0始まり)のポケットの表はpos = i、同じポケットの裏は右からi番目に相当するので2n-1 – i
※ 再掲:2n-1 = そのファイルの最後(右端の裏)
例(n=3):
-
pos=0のペア →pairPos = 5(1 ↔ 6) -
pos=1のペア →pairPos = 4(2 ↔ 5) -
pos=2のペア →pairPos = 3(3 ↔ 4) -
pos=3のペア →pairPos = 2(裏側のカードが表側に対応)
④ 全体での番号に戻す
const result = file * 2 * n + pairPos + 1;
-
file * 2 * nでそのファイルの先頭(1始まりで言うとfile*2n + 1)に相当するセットを作る。 -
pairPosを足して ファイル内での位置 を指定し、最後に+1で 1始まりの番号に戻す。 -
そのファイルの 「基準位置」=
file*2*nに、裏の位置pairPosを足して、さらに+1で1始まりに戻す。 -
つまり「ファイル先頭 + ファイル内の位置 + 1」= 裏側の元の番号。
例(n=3):
-
m=1→file=0, pos=0, pairPos=5→result = 0 + 5 +1 = 6→ 1番の裏は6番! -
m=4→file=0, pos=3, pairPos=2→result = 0 + 2 +1 = 3 -
m=7→file=1, pos=0, pairPos=5→result = 6 + 5 +1 = 12
📝 まとめ
-
file:その名刺が入っているファイル(0始まり) -
pos:ファイル内での位置(左から 0 .. 2n-1) -
pairPos:同じポケットの裏側はファイル内で反転した位置 →2n-1-pos -
result:そのファイルの先頭にpairPosを足して 1始まりに戻したもの