TopCoder Open Round 1 Medium TwoRegisters
問題概要
二つのレジスタX,Yを持つ単純なコンピュータがある。
X,Yの初期値は1で、次の二つの命令が可能である。
- X
- XにYを足す
- Y
- YにXを足す
このとき与えられた数rをレジスタXに作るための、長さ最小で、かつその中でアルファベット順で最初にくる命令列を求めよ。
r≦10^6である
解法
Xがrになる直前のYの値を決めると命令が復元できる。
……一意にorz
メモ化する必要ねーじゃん。
なので、Yレジスタの値を1からrまで試して(ただしGCDが1でないものはありえないので飛ばす)、命令が最小の長さになるようなYレジスタを列挙して、
次に具体的に命令を作る。命令の長さはせいぜい100とか200のようなので単純な実装でよい。
ソースコード
int calc(int a,int b) { if(a<b)swap(a,b); if(b==0)return 0; return calc(b,a%b)+a/b; } void build(string &s,int x,int y) { if(x==1&&y==1)return; s.pb(x>y?'X':'Y'); if(x>y)build(s,x-y,y); else build(s,x,y-x); } class TwoRegisters { public: string minProg(int r) { if(r==1)return string(); int mnlen=inf,len; vi mni; for(int i=1;i<r;i++)if(__gcd(r,i)==1) { len=calc(r,i); if(len<mnlen)mni.clear(),mnlen=len; if(len==mnlen)mni.pb(i); } string ans; rp(i,mni) { string tmp; build(tmp,r,mni[i]); reverse(all(tmp)); if(ans.empty()||tmp<ans)ans=tmp; } return ans; } };