AtCoder Grand Contest 018 B - Sports Festival
「3月中に毎日1問解いて黄色になろう!」キャンペーン10問目。
問題概要
N人の人はM種類のスポーツの内開催されていて一番好きなスポーツにしか出ない。一番スポーツがばらけるように開催するかしないか選んだ時、一番人が集まるスポーツの人数を求めよ。
https://beta.atcoder.jp/contests/agc018/tasks/agc018_b
解説
2部グラフですね。フローが流せます。
全部容量1にして、全体をまとめるs,tは特に考えません。N人の状態について、「何番目まで得意なスポーツがやっているか見たか」という情報を持たせます。この優先順序でフローを流します。
流した後、M個のスポーツについて最も集まっている=逆辺の本数が最も多いものの個数をretとします。retより小さい解を求めるにあたって、ret以上の本数が流れているスポーツを開催する理由はありません。フラグを折った後に、逆辺に沿って流して、次の順位を見ます。
このようにすると、M個のスポーツが無くなる前にループが終わる=M個のスポーツ全てについてret未満の辺が入っているということになるので、前に戻ってそれより小さい解を探し、最後まで見終わってしまったらそこでプログラムを止めます。
計算量は一番外側(retを更新)が最悪O(M)、その内側のチェックO(M)、イテレータ部分O(M)、戻す作業O(N)なのでO(M2(M+N))という感じでしょうか。ちょっと分かりませんが…
https://beta.atcoder.jp/contests/agc018/submissions/2252848
import sys n,m=map(int,input().split()) a=[list(map(int,input().split())) for i in range(n)] for i in range(n): for j in range(m): a[i][j]-=1 itr=[0 for i in range(n)] opend=[True for i in range(m)] back=[[] for i in range(m)] for i in range(n): back[a[i][0]].append(i) ret=10**9 while True: tmp=0 for i in range(m): tmp=max(tmp,len(back[i])) ret=min(ret,tmp) for i in range(m): if len(back[i])>=ret: opend[i]=False for j in range(len(back[i])): source=back[i][j] while not opend[a[source][itr[source]]]: itr[source]+=1 if itr[source]==m: print(ret) sys.exit() back[a[source][itr[source]]].append(source) back[i]=[]
感想
自然とフローが浮かんだので進歩?