問題はこちら
No.555 世界史のレポート - yukicoder
DP解法との賢い数学解法がある
・DP解
N文字以上を作る最小コスト=min{ちょうどK文字を作る最小コスト|K≧N}
K文字を作る際には、その過程で必ずK/2文字以上の状態を経由するので、Kの上限は2Nとしてよい。
・もらうDP
ちょうどi文字を作るための最小コストをd[i]とする。d[1]=1。
ちょうどi文字を作るためには、最後にコピーを行う際の文字数kはiの約数でなければならない。
このときペーストはi/k-1回行われるので、コストはd[k]+C+(i/k-1)*Vとなる
約数列挙にO(√i)かかるので全体で、Σ√i=O(N√N)
・配るDP
上の解法を配るDPに書き換えるだけ
j≧2に対して、d[i*j]=min(d[i*j],d[i]+C+(j-1)*V)とすればよい
ΣN/i=O(NlogN)
n,c,v; dp[100010]; ans=1e9; main(){ scanf("%d%d%d",&n,&c,&v); for(int i=0;i<2*n+5;i++)dp[i]=1e9; dp[1]=0; for(int i=1;i<n;i++)for(int j=2;i*j<2*n;j++)dp[i*j]=fmin(dp[i*j],dp[i]+c+(j-1)*v); for(int i=n;i<2*n;i++)ans=fmin(ans,dp[i]); printf("%d",ans); }
・O(logN)の賢い解法
操作は「コピー、(ペースト)×a1、全体をコピー、(ペースト)×a2、全体をコピー、……」となる。
このときペーストの回数を順にaiとすれば、最終的な文字数はとなる。
各kに対して最小コストを考える。
の条件下で
を最小化する問題を考える。
kは固定しているので、これは結局
を最小化することと同じ。
以下1+aiをaiと改めて書く。
さて、相加相乗平均の関係から、Σaiの最小値はaiたちが等しい値をとるとき(∀i,j.|ai-aj|≦1)に達成される。
よって、aiたちはfloor(N^(1/k))及びそれ+1の値を、Πai≧Nとなるように取ればよいことがわかり、最小値はO(1)で求められる。
またai≧2よりΠai≧2^kなのでkはlog2(N)+1まで調べればよく、全体ではO(logN)で求める事ができる。
n,c,v; a,b,t; ans=1e9; main(){ scanf("%d%d%d",&n,&c,&v); for(int k=1;k<log2(n)+1;k++){ a=pow(n,1.0/k);//aiたちの小さい方の値 b=(log(n)-k*log(a))/(log(a+1)-log(a))+1-1e-9;//大きい方の個数 //+1-1e-9は切り上げのため t=(c-v)*k+v*(a*k+b); ans=fmin(ans,t); } printf("%d",ans); }
・発想の転換
実はdfsでも解ける
f(現在の文字数,クリップボードの文字数)=必要な残りコスト
という関数を考えると
n,c,v; f(a,b){ if(a>=n)return 0; return fmin(f(a+b,b)+v,f(a+a,a)+c+v); } main(){ scanf("%d%d%d",&n,&c,&v); printf("%d",f(1,1)+c); }
と書くことができる
最大ケースのN=50000でもfは168685291回≒1.6×10^8回しか呼び出されないらしく間に合う。
これをギュッとする
n,c,v; f(a,b){ return a<n?fmin(f(a+b,b),f(a+a,a)+c)+v:c; } main(){ scanf("%d%d%d",&n,&c,&v); printf("%d",f(1,1)); }
return文を省略
x,n,c,v; f(a,b){ x=a<n?fmin(f(a+b,b),f(a+a,a)+c)+v:c; } main(){ scanf("%d%d%d",&n,&c,&v); printf("%d",f(1,1)); }
105B