投げるたびにボールを受け取る子供の番号を求めるC++プログラム
問題の概要
ある数 n が与えられ、n 人の子供が円形に立っているとします。子供たちには時計回りに 1 から n までの番号が付けられており、最初は 1 番の子供がボールを持っています。
まず 1 番の子供が、時計回りに隣にいる子供(2 番)へボールを投げます。次に 2 番の子供が、その隣の子供(4 番)へ投げ、続いて 4 番の子供が 7 番の子供へと投げます。このように、投げるたびにボールが進む距離が 1 つずつ増えていきます。ボールが投げられる際には、円の一周をまたいで先頭に戻ることもあります。
ゲームの途中でボールを受け取らない子供もいるため、各投擲の後にボールを受け取る子供の番号をすべて求める必要があります。
例えば、入力が n = 10 の場合、出力は [2, 4, 7, 1, 6, 2, 9, 7, 6] となります。
アルゴリズムの手順
この問題は、次の手順で解くことができます。
- 現在位置を表す変数 p を 1 で初期化します。
- i を 1 から n-1 まで順に処理し、各ステップで p に i を加算します。
- p を n で割った余りに更新します。余りが 0 になった場合は n に置き換えます(番号は 1 から始まるため)。
- 各ステップの p の値を出力します。
p := 1 for initialize i := 1, when i < n, update (increase i by 1), do: p := p + i p := p mod n if not p is non-zero, then: p := n print p
仕組みの解説
i 回目の投擲では、ボールは時計回りに i 人先の子供へと渡されます。つまり、k 回目の投擲が終わった時点でのボールの位置は、「1 から k までの累積和を n で割った余り」(ただし 0 の場合は n)として表せます。この性質を利用することで、シミュレーションを単純なループで実装でき、計算量 O(n) で各投擲後のボールの受け取り手を効率的に列挙できます。
C++での実装例
理解を深めるために、以下の実装例を見てみましょう。
#include <bits/stdc++.h>
using namespace std;
void solve(int n){
int p = 1;
for (int i = 1; i < n; i++){
p += i;
p %= n;
if (!p)
p = n;
printf("%d, ", p);
}
}
int main(){
int n = 10;
solve(n);
}入力
10
出力
2, 4, 7, 1, 6, 2, 9, 7, 6,
-
C++で円をN回カットしたときのピース数を計算する方法
問題の概要整数Nが与えられます。このNは、2次元平面上の円に対して加える「カット(切り込み)」の回数を表します。1回のカットによって円は2つに分けられるため、N回のカットを行った後に円がいくつのピースに分割されるかを求めるのが、この問題の目的です。計算式この問題はとてもシンプルで、次の式で答えを求めることができます。ピースの数 = 2 × カットの回数(N)各カットが円の中心を通って切断されると考えると、カット1回ごとにピースが2つずつ増えていくため、この式が成り立ちます。具体例入力: N = 1出力: 円のピース数: 2説明: 1回のカットで、円はちょうど2つの半分に分けられます。入力: N
-
C++で数値を除算した際の小数点以下の桁数を数える方法
2つの整数 num1 と num2 が与えられたとき、num1 を num2 で除算し、その結果の小数点以下の桁数を求めるのが本記事のテーマです。具体例入力 − num1 = 2, num2 = 5出力 − count is 1説明 − 2 を 5 で割ると 2/5 = 0.4 となり、小数点以下の桁数は 1 桁であるため、count は 1 になります。入力 − num1 = 2, num2 = 0出力 − Floating point exception (core dumped)説明 − 任意の数を 0 で割るとエラーが発生し、プログラムは異常終了します。実装の際はゼロ除算への対処が必要