はじめての二分探索
やります。
AtCoder TagsのAtCoder_Tags_Hintという機能で二分探索
のタグがついていたので、*1
ちょうどよいと思い、練習することにしました。
Tag
をクリックすると、
と出ます。
ででどん
1≦X≦109から条件を満たすXを探します。
sortされた整数列から探すわけなので二分探索が出来そうです。
c++
にはlower_bound
やupper_bound
といった便利なライブラリがありますが、
今回は初めての二分探索なのでまずは自分でいちから作ってみます。
たしか二分探索は半分の位置を見て次調べるのが前か後ろか決めるようなものだったと思うので、
int left=0,right=1e9; int mid=(left+right)/2; //500000000
とします。
今回の問題の条件を満たすか調べます。
if(a*mid+b*d(mid)<=x)right=mid; else left=mid;
条件を満たす場合には右端を縮めて、
満たさない場合には左端を縮めます。
ちなみにd(mid)
は整数mid
の桁数を返す自作関数です。
int d(int mid){return to_string(mid).size();}
これを何回かループさせて答えを探索するはずなのですが、ループの抜け方がイマイチ分かりません。
安直に左端left
と右端right
が一致したら抜けるようにしてみましょう。
あと、デバッグ用の出力を追記したり、型をlong long
で統一させたりしています。
ll left=0,right=1e9,mid; while(right!=left) { mid=(left+right)/2; printf("%lld %lld %lld\n",left,mid,right); ll t=a*mid+b*d(mid); if(t>x)right=mid; else left=mid; } cout<<mid<<endl;
これにサンプル10 7 100
を入れると、
こうなります。
無限ループです。
ただ答えの9
には辿り着けているので嬉しいです。
実際10分くらい9にすら辿り着けなくて悪戦苦闘していたので十分嬉しいのです。
ループ
正しいループの抜け方を考えます。
9と10が境目です。
境目の左側は条件を満たし、右側は条件を満たしませんつまり、このleft
が条件を満たす最大値を示します。
while
の抜ける条件をright-left!=1
として最後にleft
を出力させてみます。
ll left=0,right=1e9,mid; while(right-left!=1) { mid=(left+right)/2; printf("%lld %lld %lld\n",left,mid,right); ll t=a*mid+b*d(mid); if(t>x)right=mid; else left=mid; } cout<<left<<endl;
こうなります。
きれいですね。
他のサンプルでも試しましたが、サンプル2以外は上手く機能しました。
サンプル2は2 1 100000000000
です。
これはお店に売られている最大整数が買えるパターンです。
このパターンも拾えるように修正をして提出をします。
提出
using namespace std; ll d(ll mid){return to_string(mid).size();} main() { ll a,b; ll x; cin>>a>>b>>x; //まず最大整数を買えるのかどうか調べとく ll c=1e9; if(a*c+b*d(c)<=x) { cout<<c<<endl; return 0; } ll left=0,right=1e9,mid; while(right-left!=1) { mid=(left+right)/2; //printf("%lld %lld %lld\n",left,mid,right); ll t=a*mid+b*d(mid); if(t>x)right=mid; else left=mid; } cout<<left<<endl; }
無事ACしました。
とりあえず二分探は寒色系を目指すにあたっての必須科目だと思うので自分の手足のように使いこなせるようになりたいです。
対戦ありがとうございました。
ここまでお読みいただきありがとうございました。