コンピューターゲームが大好きな Mehmet は、過去 10 時間ずっと Immortal Grasshopper をプレイしています。Immortal Grasshopper は、空手の達人であるバッタをキャラクターとして、ボタンで操作するコンピューターゲームです。対戦相手はコンピューターが操作するアリです。
バッタは全部で $S$ 種類($1 \le S \le 4$)の異なる攻撃を繰り出せます。これらの攻撃には、アルファベットの最初の $S$ 文字を使った名前が付いています。例えば:
- $S = 2$:攻撃の名前は $A$、$B$ です。
- $S = 4$:攻撃の名前は $A$、$B$、$C$、$D$ です。
相手に命中した攻撃は、それぞれ 1 ダメージを与えます。
ゲームのルールは知っていますが、Mehmet は 10 時間プレイした後に眠ってしまったため、操作方法を教えてくれる人がいません。机の上に $N$ 個のボタンが付いたコントローラーを見つけました(ボタンには $0$ から $N - 1$ までの番号が付いています)。しばらくプレイして、各ボタンが $S$ 種類の攻撃のいずれかを繰り出せることは分かりましたが、どのボタンがどの攻撃に対応するかは分かりません。幸い、最初のボタンに貼られたシールのおかげで、そのボタンが $A$ 型の攻撃を繰り出すことは分かります。また、各種類の攻撃は少なくとも 1 個のボタンに割り当てられていることも分かっています。
あなたの課題は、このゲームをプレイして、コントローラーのすべてのボタンがどの種類の攻撃を繰り出すかを特定することです。
ゲームは以下のようにプレイします。相手のアリを攻撃するために、「コンボ」と呼ばれる、ボタンを連続して押す組み合わせを使います。コンボは次のように動作します:
- 任意の個数のボタンを順に押してコンボを作ります。
- コンボ内では、任意の順番でボタンを押せます。
- 1 回のコンボ内で、同じボタンを複数回押せます。
- コンボの長さは最大 $400$ です。
ゲームはあなたの操作を受け取り、最後に与えたダメージの合計を表示します。
コンボがアリに与えるダメージは、以下のように計算されます。$t(X)$ を、アルファベット順で攻撃 $X$ の直後に来る攻撃とします。最後の文字で表される攻撃については、$t(X)$ を攻撃 $A$ と定義します。例えば:
- $S = 4$ のとき:$t(A) = B$、$t(B) = C$、$t(C) = D$、$t(D) = A$ です。
- $S = 2$ のとき:$t(A) = B$、$t(B) = A$ です。
相手のアリは、ずる賢い戦術であなたの攻撃の一部を防ぎます。より具体的には、直前に成功し(防がれず)、ダメージを与えた攻撃が $X$ で、次の攻撃として $t(X)$ を使うと、アリはその $t(X)$ 攻撃を防ぎ、ダメージは与えられません。アリが防げなければ、アリに 1 ダメージを与えます(最初の攻撃は必ずダメージを与えます)。
あなたの目標は、コンボの総回数を最小限にして、すべてのボタンが繰り出す攻撃を特定し、報告することです。
実装の詳細
以下の関数を実装してください:
std::string olumsuz_cekirge(int N, int S)
- $N$ はコントローラーのボタンの個数です。
- $S$ はゲームにおける攻撃の種類数です。
- この関数は、各ボタンに割り当てられた攻撃を表す、長さ $N$ の文字列を返す必要があります。
この処理の際、以下の kombo 関数を使用できます:
int kombo(std::vector<int> buton)
olumsuz_cekirgeの 1 回の呼び出し中に、kombo関数を呼び出せるのは最大 $400$ 回です。- 配列
butonは、コンボで使用したいボタンの番号を指定します。 - 配列
butonの値は、$0$ から $N - 1$ までの整数でなければなりません。 - 配列
butonの長さは最大 $400$ です。
各テストでは、同じ $N$ と $S$ の値を持つ $T$ 個の独立したシナリオを解く必要があります。各シナリオに対して、olumsuz_cekirge 関数が 1 回呼び出されます。
制約
- $1 \le T \le 100$
- $4 \le N \le 400$
- $1 \le S \le 4$
- 採点プログラムは適応的ではありません。具体的には、入力は
olumsuz_cekirge関数が呼び出される前に固定され、操作中には変化しません。
小課題
いずれかのシナリオで、上記の条件を満たさない引数を指定して kombo 関数を呼び出した場合、または olumsuz_cekirge 関数が誤った結果を返した場合、第 $i$ 小課題の得点は $0$ です。それ以外の場合、得点は以下の式に等しくなります:
$$P_i \times \min\left(1,\left(\frac{400-q}{400-Q_i}\right)^3\right)$$
- $P_i$ は、第 $i$ 小課題で獲得できる合計点数です。
- $q$ は、第 $i$ 小課題の全テストのすべてのシナリオの中で、
kombo関数の呼び出し回数が最大となるシナリオでの呼び出し回数です。 - $Q_i$ の値は、小課題の説明に示されています。
| 小課題 | 点数 | 追加の制約 | 依存する小課題 |
|---|---|---|---|
| 1 | 1 | $S = 1, Q_1 = 0, P_1 = 1$ | — |
| 2 | 15 | $S = 2, Q_2 = 50, P_2 = 15$ | — |
| 3 | 40 | $S = 3, Q_3 = 80, P_3 = 40$ | — |
| 4 | 44 | $S = 4, Q_4 = 103, P_4 = 44$ | — |
入出力例
olumsuz_cekirge(6, 3)
これらの例では、各ボタンに対応する攻撃は順に ABCBBA なので、この関数はこの文字列を返す必要があります。
kombo([0, 1, 2])
上記の呼び出しは $2$ を返します。このコンボの攻撃は $ABC$ です。最初の $A$ 攻撃は命中します。$B$ 攻撃は防がれます。直前にダメージを与えた攻撃は $A$ なので、アリは $C$ 攻撃を防げず、ダメージを受けます。
kombo([0, 0, 0])
上記の呼び出しは $3$ を返します。このコンボの攻撃は $AAA$ です。すべての $A$ 攻撃がダメージを与えます。
kombo([5, 1, 3, 2, 2, 0])
上記の呼び出しは $3$ を返します。このコンボの攻撃は $ABBCCA$ です。最初の $A$ 攻撃は命中します。$B$ 攻撃は防がれます。直前に命中した攻撃はまだ $A$ なので、2 回目の $B$ 攻撃も防がれます。1 回目と 2 回目の $C$ 攻撃はダメージを与えます。直前に命中した攻撃が $C$ なので、相手のアリは $A$ を予期し、最後の $A$ 攻撃は防がれます。
kombo([2, 2, 1])
上記の呼び出しは $3$ を返します。このコンボの攻撃は $CCB$ です。すべての攻撃がダメージを与えます。
サンプル採点プログラム
サンプル採点プログラムは、以下の形式で入力を読み込みます。$C_i$ は、第 $i$ シナリオの各ボタンに対応する隠された攻撃を表す、長さ $N$ の文字列です:
$$\begin{gathered}T\ N\ S\\C_1\\C_2\\\ldots\\C_T\end{gathered}$$
各シナリオについて、次の内容を出力します。シナリオが正しく解かれた場合は、使用した操作の総回数を出力します。何らかのエラーがある場合は、以下のエラーメッセージのいずれかを出力します:
Yanlis cevap (Wrong answer) Buton asildi (Button limit exceeded) Yanlis buton (Invalid button) Kombo asildi (Combo limit exceeded)
Yanlis cevap (Wrong answer):いずれかのボタンの答えに誤りがある場合に出力されます。Buton asildi (Button limit exceeded):kombo関数の 1 回の呼び出しで $400$ 個を超えるボタンが使われた場合に返されます。Yanlis buton (Invalid button):kombo関数の呼び出しで、いずれかのボタンの番号が $0$ から $N - 1$ までの数でない場合に返されます。Kombo asildi (Combo limit exceeded):olumsuz_cekirge関数の 1 回の呼び出し中にkombo関数が $400$ 回を超えて呼び出された場合に返されます。