公式
C - Sneaking Glances 解説
by
C - Sneaking Glances 解説
by
physics0523
座標が正なら負の方向に、負なら正の方向に移動するという貪欲法は失敗します。
例えば \(L=(40,50,90,1,1,1)\) の時、貪欲法に従うと座標 \(0\) を \(3\) 回通過することになりますが、最初に負・負・正の方向を選択することで座標 \(0\) を \(5\) 回通過することができます。
\(N\) が小さいことを利用しましょう。
「正か負か」という \(2\) 択を \(N\) 回行う、全部で \(2^N\) 通りの選択を全探索する場合、 bit全探索 (解説記事) が便利です。
- \(i=0,1,\dots,2^N-1\) について、以下を繰り返す。
- \(j=0,1,\dots,N\) について、 \(i\) を \(2\) 進表記した時に \(2^j\) の位が \(0\) なら負の方向に、 \(1\) なら正の方向に移動することにする。
シミュレートの際、 \(0.5\) という数を扱うのは不便なので、座標・移動距離ともに \(2\) 倍して考えると全てを整数で取り扱うことができて実装が楽です。
つまり、最初に座標 \(1\) 、全ての移動を距離 \(2L_i\) だけ行うことにすればよいです。
また、 \(0\) を跨いだがどうか判定するには、移動前の座標を \(x\) 、移動後の座標を \(y\) として \(xy<0\) かどうかで判定できます。そのまま判定するとオーバーフローのおそれがあるため、符号を保ったまま値を \(-1,0,1\) のいずれかに圧縮してから判定するとよいです。
時間計算量は \(O(2^N N)\) です。
実装例 (C++):
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll sgn(ll x){
if(x>0){return 1;}
else if(x<0){return -1;}
return 0;
}
int main(){
ll n;
cin >> n;
vector<ll> l(n);
for(auto &nx : l){
cin >> nx;
nx*=2;
}
ll best=0;
for(ll i=0;i<(1ll<<n);i++){
ll pos=1,cur=0;
for(ll j=0;j<n;j++){
ll npos=pos;
if(i&(1ll<<j)){npos+=l[j];}
else{npos-=l[j];}
if(sgn(pos)*sgn(npos)<0){cur++;}
pos=npos;
}
best=max(best,cur);
}
cout << best << "\n";
return 0;
}
投稿日時:
最終更新:
