I proved that the 52-step answer is optimal with brute force.
C++ code:
//#define _GLIBCXX_DEBUG
#include <x86intrin.h>
#include <cstring>
#include <iostream>
#include <streambuf>
#include <bitset>
#include <cstdio>
#include <atomic>
#include <vector>
#include <algorithm>
#include <cmath>
#include <climits>
#include <random>
#include <set>
#include <list>
#include <map>
#include <unordered_map>
#include <deque>
#include <stack>
#include <queue>
#include <string>
#include <iomanip>
#include <unordered_set>
#include <thread>
const char N = 7;
const char i0 = (N/2)*N + N/2;
const char i1 = N*N-1, i2 = N-1, i3 = 0, i4 = N*N-N;
std::vector<short> dbfs(int64_t mask, int from)
{
std::vector<short> dists(N*N, 255);
dists[from] = 0;
std::queue<char> q;
q.push(from);
while(!q.empty())
{
char cur = q.front(); q.pop();
short cd = dists[cur];
char x = cur % N, y = cur / N;
if(x != 0)
{
char ni = (x-1) + (y)*N;
if((mask & (1ll<<ni)) && dists[ni] > cd+1) dists[ni] = cd+1, q.push(ni);
//if(dists[ni] <= cd + 1) continue;
//dists[ni] = cd + 1; q.push(ni);
}
if(x != N-1)
{
char ni = (x+1) + (y)*N;
if((mask & (1ll<<ni)) && dists[ni] > cd+1) dists[ni] = cd+1, q.push(ni);
//dists[ni] = cd + 1; q.push(ni);
}
if(y != 0)
{
char ni = (x) + (y-1)*N;
if((mask & (1ll<<ni)) && dists[ni] > cd+1) dists[ni] = cd+1, q.push(ni);
//dists[ni] = cd + 1; q.push(ni);
}
if(y != N-1)
{
char ni = (x) + (y+1)*N;
if((mask & (1ll<<ni)) && dists[ni] > cd+1) dists[ni] = cd+1, q.push(ni);
//dists[ni] = cd + 1; q.push(ni);
}
}
return dists;
}
bool dfs(int64_t mask, char at, char prev, std::vector<char>& marks)
{
marks[at] = true;
char x = at % N, y = at / N;
if(x != 0)
{char ni = (x-1) + (y)*N; if((mask & (1ll<<ni)) && ni != prev && ((marks[ni] || dfs(mask, ni, at, marks)))) return true;}
if(x != N-1)
{char ni = (x+1) + (y)*N; if((mask & (1ll<<ni)) && ni != prev && ((marks[ni] || dfs(mask, ni, at, marks)))) return true;}
if(y != 0)
{char ni = (x) + (y-1)*N; if((mask & (1ll<<ni)) && ni != prev && ((marks[ni] || dfs(mask, ni, at, marks)))) return true;}
if(y != N-1)
{char ni = (x) + (y+1)*N; if((mask & (1ll<<ni)) && ni != prev && ((marks[ni] || dfs(mask, ni, at, marks)))) return true;}
return false;
}
bool treeq(int64_t mask)
{
std::vector<char> marks(N * N);
if(dfs(mask, i0, -1, marks)) return false;
for(int i = 0; i < N*N; i++) if(marks[i] == 0 && (mask & (1ll<<i))) return false;
return true;
}
int main()
{
setbuf(stdout, 0);
const int64_t expected = 1ll << (N*N-5);
printf("%lld\n", expected);
volatile int64_t iters = 0;
volatile short best_ans = 0;
auto starttime = std::chrono::high_resolution_clock::now();
const int64_t checkfor = 1l<<i0 | 1l<<i1 | 1l<<i2 | 1l<<i3 | 1l<<i4;
#pragma omp parallel for schedule(guided)
//for(int64_t mask = 1ll<<(N*N); mask --> 0;)
for(int64_t mask = checkfor; mask < 1ll<<(N*N); mask++)
{
if(__builtin_popcountll(mask & checkfor) != 5) continue;
if(iters % 10000 == 0 && iters != 0)
{
//printf("%.5lf left\n", iters * 100.0 / expected),
//printf("%ld done\n", iters);
auto cur = std::chrono::high_resolution_clock::now();
int64_t ns = std::chrono::duration_cast<std::chrono::nanoseconds>(cur - starttime).count();
//printf("%ld\n", ns);
int64_t tofinish = ns / iters * expected;
//printf("%lld\n", expected);
printf("%.5lf%% done (%ld), %.5lf second to finish\r", iters * 100.0 / expected, iters, (tofinish - ns) / 1e9);
}
#pragma omp atomic
iters++;
if(iters < 1099184650000) continue;
if(!treeq(mask)) continue; //must be a connected tree
std::vector<short> d0 = dbfs(mask, i0);
std::vector<short> d1 = dbfs(mask, i1);
std::vector<short> d2 = dbfs(mask, i2);
std::vector<short> d3 = dbfs(mask, i3);
std::vector<short> d4 = dbfs(mask, i4);
int64_t ans = std::min<int>({
d0[i1] + d1[i2] + d2[i3] + d3[i4],
d0[i1] + d1[i2] + d2[i4] + d4[i3],
d0[i1] + d1[i3] + d3[i2] + d2[i4],
d0[i1] + d1[i3] + d3[i4] + d4[i2],
d0[i1] + d1[i4] + d4[i2] + d2[i3],
d0[i1] + d1[i4] + d4[i3] + d3[i2],
d0[i2] + d2[i1] + d1[i3] + d3[i4],
d0[i2] + d2[i1] + d1[i4] + d4[i3],
d0[i2] + d2[i3] + d3[i1] + d1[i4],
d0[i2] + d2[i3] + d3[i4] + d4[i1],
d0[i2] + d2[i4] + d4[i1] + d1[i3],
d0[i2] + d2[i4] + d4[i3] + d3[i1],
d0[i3] + d3[i1] + d1[i2] + d2[i4],
d0[i3] + d3[i1] + d1[i4] + d4[i2],
d0[i3] + d3[i2] + d2[i1] + d1[i4],
d0[i3] + d3[i2] + d2[i4] + d4[i1],
d0[i3] + d3[i4] + d4[i1] + d1[i2],
d0[i3] + d3[i4] + d4[i2] + d2[i1],
d0[i4] + d4[i1] + d1[i2] + d2[i3],
d0[i4] + d4[i1] + d1[i3] + d3[i2],
d0[i4] + d4[i2] + d2[i1] + d1[i3],
d0[i4] + d4[i2] + d2[i3] + d3[i1],
d0[i4] + d4[i3] + d3[i1] + d1[i2],
d0[i4] + d4[i3] + d3[i2] + d2[i1]
});
if(ans > best_ans)
{
#pragma omp critical
if(ans > best_ans)
{
best_ans = ans; printf("\n%d (%ld)\n", ans, mask);
for(int el : d0) printf("%d ", el); printf("\n");
for(int el : d1) printf("%d ", el); printf("\n");
for(int el : d2) printf("%d ", el); printf("\n");
for(int el : d3) printf("%d ", el); printf("\n");
for(int el : d4) printf("%d ", el); printf("\n");
}
//for(;;) ;
}
}
}