次に解いたのは、Bランク・スキルチェック過去問の「名刺バインダー管理」です。
一発正答でいけましたが、なかなか法則をプログラムに落とし込めず、40分もかかってしまいました。
これは、シミュレーション問題ではなく、法則を見つけることで解くことができましたが、実際に名刺バインダーの配列を作って番号を配置することでも解ける気がしました。
ひとまず、悩みつつ書いたコードは以下の通りです。
#include <stdio.h>
#include <math.h>
int main(void){
char buf[100];
int pocket_num, target_num;
fgets(buf, sizeof(buf), stdin);
sscanf(buf, "%d %d", &pocket_num, &target_num);
//printf("%d %d", pocket_num, target_num);
/*
ポケットの数が3の場合の、裏表の対応
1 <-> 6 => 足したら7 (= 3 * (2 * 1) + 1)
2 <-> 5 => 7
3 <-> 4 => 7
4 <-> 3 => 7
5 <-> 2 => 7
6 <-> 1 => 7
7 <-> 12 => 19 (= 3 * (2 * 3) + 1)
...
10 <-> 9 => 19
...
14 <-> 17 => 31 (= 3 * (2 * 5) + 1)
つまり、解がxの場合、
target_num + x = pocket_num * (2 * magic_num) + 1
1ページ目は、右辺は7 -> magic_num = 1
2ページ目は、右辺は7 -> magic_num = 1
3ページ目は、右辺は19 -> magic_num = 3
4ページ目は、右辺は19 -> magic_num = 3
5ページ目は、右辺は31 -> magic_num = 5となる
*/
// 何ページ目に配置されるかは、target_num / pocket_numの解の切り上げ
int page = ceil((float)target_num / pocket_num);
// 偶数ページ目のmagic_numは、そのページ数を-1する
int magic_num = page;
if(page % 2 == 0) {
magic_num = page - 1;
}
//printf("magic [%d]", magic_number);
// target_num + x = pocket_num * (2 * magic_num) + 1を
// xについて解いた式で求める
int x = pocket_num * (2 * magic_num) + 1 - target_num;
printf("%d\n", x);
return 0;
}
別解
また、以下のようにも考えられそうです。
ポケットの数が3のとき、
1 => 6 (= 3 * 2 + 1 - 1) 差は-5
2 => 5 (= 3 * 2 + 1 - 2) 差は-3
3 => 4 (= 3 * 2 + 1 - 3) 差は-1
4 => 3 (= 3 * 2 + 1 - 4) 差は1
5 => 2 (= 3 * 2 + 1 - 5) 差は3
6 => 1 (= 3 * 2 + 1 - 6) 差は5
という対応を考え、7以降は、
7 => 7 % (3 * 2) = 1
8 => 8 % (3 * 2) = 2
9 => 9 % (3 * 2) = 3
13 => 13 % (3 * 2) = 1
...
これで、target_numの該当する典型的な位置がわかれば、その数字から、上記の差を足すことで求められそうです。
終わりに
手元で例を書いていくことで、こういう変化があるなということはわかったのですが、それをプログラムへ落とし込むが難しかったです。
たぶん、もっと簡単に解く方法があるのだと思いますが、良い頭の体操にはなりましたのでよしとします。
以上、ここまで読んでいただきありがとうございました。