TopCoder SRM 392 Div1 Medium RoundAboutCircle
問題
1〜Nの数字が円周上にならんでいる。
マークを使って次のような操作を行う。
- 数字ひとつにマークを置く。
- マークが置かれている数字の、各桁の和を求め、その分だけマークを進める。
- 同じ数字に2度訪れた時点で終了する。
- これまでに訪れた数字の個数が得点になる
このとき、得られる最大の得点はいくつか求めよ。
制約条件
N≦200000
方針
数字を頂点として、マークが移動する数字から数字へ辺を張ったグラフを考える。
ある数字がループに含まれていたら、その数字からスタートしたときの得点はそのループの大きさになる。
ループの外にある数字は、ループまでの距離+ループの大きさが得点になる。
なのでループ検出を実装すればよい。
自分の実装は、深さ優先探索をしながら、今までに訪問した頂点および、
訪問した頂点を何番目に訪れたかを記憶しておいて、
同じ頂点に2度きたらそこまでがループなので、訪問順からループに含まれる頂点の得点を決めるという感じ。
ソースコード
int N; int d(int n){ int res=0; for(;n;n/=10)res+=n%10; return res; } int sc[200001],ord[200001],q[200001],sz; int calc(int n,int len){ if(sc[n]>0)return sc[n]; if(ord[n]>0){ for(int i=sz-1;;i--){ sc[q[i]]=len-ord[n]; if(q[i]==n)break; } return sc[n]; } ord[n]=len; q[sz++]=n; int tmp=calc((n+d(n+1))%N,len+1); if(sc[n]>0)return sc[n]; return sc[n]=tmp+1; } class RoundAboutCircle { public: int maxScore(int N) { memset(sc,0,sizeof(sc)); ::N=N; int ans=0; rep(i,N){ ans=max(ans,calc(i,1)); rep(j,sz)ord[q[j]]=0; //探索した頂点は訪問順を初期化 sz=0; } return ans; } };