公式解説

By
yama_can

小課題 1

bit全探索を行うと正解できます。
計算量は O(N2N)\mathcal O(N 2^N) となり十分高速です。

小課題 2

歌えるのは 2人 までなので、歌う人を1, 2人選び全探索すればよいです。

小課題 3

AiA_i がその順番以上になるように選べばよいです。
これは、以下の DP によって実装できます。

dpi,j=(i 人目まで見て j 人選べるか)dp_{i, j} = \text{(i 人目まで見て j 人選べるか)}

小課題 5

DP 配列の2個目の添字は大きければ良いことが示せます。
よって、ii 番目まで見たときの最大の人数を保持すればよいです。

小課題 4

小課題 5 の考え方を用いると、添字を一つにできました。
ここで、あと何人選べるか、という添字を追加し、その時の最大の人数を保持すれば解くことができます。

満点解法

満点を取るには、DP による考えをさらに単純化する必要があります。

まず、 n+1n+1 人で歌えるなら nn 人で歌える事を示します。
これは、歌っている中で一番上手い人を外せば良いです。
まだ歌っている nn 人にとって、「右の歌う人の条件」についての人数は変わらず、「左の歌う人の条件」については人が減っているので成り立ちます。
よって nn 人で歌えるか判定できれば二分探索できます。

二分探索については上手い人から順に、 ii 人目の人を自分より上手い人(既に追加した人)を kk として kkAiA_i 人未満で、 nkn - kBiB_i より大きいなら追加すればよいです。

証明

この操作が成功したとき nn 人が可能であることは明らかです。
また、i<ji<jii を選択すると jj が選択できなくなる場合でも、双方の重みは同じなので常に選択するのが良いです。
よって、この行動は nn 人にするための最善です。

この行動により追加した人数が nn 人となれば、 nn 人が達成可能であることがわかります。

実装コード(C++, 81ms)