ABC200のD問題 Happy Birthday! 2 を工夫して解く
残念ながらABC200のD問題を解けなかったので、解説しつつふりかえります。
問題の概要
N 個の正整数からなる数列 が与えられます。 以下の条件を全て満たす 2 つの数列
が存在するか判定し、存在する場合はひとつ出力してください。
(省略)
を200で割ったあまりと、を200で割ったあまりが等しい
の中から200で割った余りが等しくなる足し算の組み合わせが2組以上あるかどうか判定して、あればそれを出力、なければ'No'と出力する問題です。
難しいポイント
Nが最大で200なのですが、足し算の組み合わせを単純に考えるととなり、とてつもない数になります。
何かに気づいて間引かないといけないです。
解き方の方針
公式解説の「鳩の巣原理」は気づかなかったです。。。
余りのパターンとしては200で割ったあまりなので、0から199の200通りしかありません。
なので少なくとも201個合計のパターンあれば、2組のパターンが存在してそれが答えになります。
それを考えると、ビット全探索でまでやる必要はなく、数列の中に同じ数字はないのでまで全探索すれば必ず答えはあるはずです。
なければ'No'と出力します。
実装
2次元配列で、同じ余りになる組み合わせを記録しました。
例えば、ans_list[5] = [1, 2, 5] の時は、a_arr[0]+a_arr[1]+a_arr[4]の余りが5になるように記録します。
N = int(input()) a_arr = list(map(int, input().split())) # 2**8の中で答えの組み合わせがある。Nが8より小さければ全通り探索する n = min(N, 8) # 同じ余りとなる組み合わせのリストを保存するリスト # ans_list[5] = [1, 2, 5] はa_arr[0]+a_arr[1]+a_arr[4]の余りが5になる ans_list = [[] for _ in range(200)] for bit in range(2**n): tmp_sum = 0 tmp_list = [] for i in range(n): if bit & (1 << i): tmp_sum += a_arr[i] tmp_list.append(i + 1) amari = tmp_sum % 200 # 同じ余りの組み合わせがあれば答えを出力して終了する if len(ans_list[amari]): print('Yes') print(len(ans_list[amari]), *ans_list[amari]) print(len(tmp_list), *tmp_list) exit() ans_list[amari] = tmp_list print('No')
鳩の巣原理に気づけなくても動的計画法(DP)で解く方法もあったようですが、私には時間内に実装できるほど実力が足りてないです。
もっと勉強して、気づけなくてもDPなどを駆使して解けるようになろうと思います。