前提知識
解説
幅優先探索の容量でやる。
$\text{dist}[i]=i$個目のマスに行くときの最短手数。その段階で行けないときは$-1$。
遷移方法
$i$の__builtin_popcountを$e$と置く。
①$\text{dist}[i+e]=\text{dist}[i]+1(i+e\leq n)$
②$\text{dist}[i-e]=\text{dist}[i]+1(i-e>0)$
こんな感じでやれば行ける。
queueには高々$N$回しかpushされず、__builtin_popcountの計算量は$O(1)$。よって実行時間は$O(N)$。
C++での解答例
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
//dist[i]=移動手数
//現段階で行けないときは-1
vector<int> dist(n+1, -1);
dist[1] = 1;
queue<int> que;
que.push(1);
while(!que.empty()){
int u = que.front();
que.pop();
//ここからは遷移
//__builtin_popcount(u)=uの二進数の桁が1になっている数=__builtin_popcount
//uから1手で行けるのはu+__builtin_popcountかu-__builtin_popcount
if(u + __builtin_popcount(u) <= n && dist[u + __builtin_popcount(u)] == -1) {
dist[u + __builtin_popcount(u)] = dist[u] + 1;
que.push(u + __builtin_popcount(u));
}
if(u - __builtin_popcount(u) > 0 && dist[u - __builtin_popcount(u)] == -1) {
dist[u - __builtin_popcount(u)] = dist[u] + 1;
que.push(u - __builtin_popcount(u));
}
}
cout << dist[n] << endl;
}