#pragma GCC optimize ("O3,inline,omit-frame-pointer,no-asynchronous-unwind-tables,fast-math") #include using namespace std; #define rep(i, a, n) for (int i = (a); i < (n); i++) using ll = long long; template void chmax(T& a, U b) { if (a < b) a = b; } template void chmin(T& a, U b) { if (b < a) a = b; } struct Timer { chrono::steady_clock::time_point st = chrono::steady_clock::now(); double elapsed() const { return chrono::duration(chrono::steady_clock::now() - st).count(); } }; int n, t; vector> a; mt19937 rng((unsigned)chrono::steady_clock::now().time_since_epoch().count()); const int DX[4] = {1, -1, 0, 0}; const int DY[4] = {0, 0, 1, -1}; int id(int x, int y) { return x * n + y; } ll score_path(const vector& path) { ll s = 0; for (int v : path) s += a[v / n][v % n]; return s; } bool valid_path(const vector& path) { if ((int)path.size() != t) return false; vector used(n * n, 0); for (int v : path) { if (v < 0 || n * n <= v || used[v]) return false; used[v] = 1; } rep(i, 1, (int)path.size()) { int x0 = path[i - 1] / n, y0 = path[i - 1] % n; int x1 = path[i] / n, y1 = path[i] % n; if (abs(x0 - x1) + abs(y0 - y1) != 1) return false; } return true; } vector snake_path(bool vertical, bool rev_x, bool rev_y) { vector p; p.reserve(n * n); if (!vertical) { rep(ii, 0, n) { int x = rev_x ? n - 1 - ii : ii; if (ii % 2 == 0) { rep(jj, 0, n) { int y = rev_y ? n - 1 - jj : jj; p.push_back(id(x, y)); } } else { for (int jj = n - 1; jj >= 0; jj--) { int y = rev_y ? n - 1 - jj : jj; p.push_back(id(x, y)); } } } } else { rep(jj, 0, n) { int y = rev_y ? n - 1 - jj : jj; if (jj % 2 == 0) { rep(ii, 0, n) { int x = rev_x ? n - 1 - ii : ii; p.push_back(id(x, y)); } } else { for (int ii = n - 1; ii >= 0; ii--) { int x = rev_x ? n - 1 - ii : ii; p.push_back(id(x, y)); } } } } return p; } void consider_contiguous(const vector& ham, ll& best_score, vector& best_path) { if ((int)ham.size() < t) return; ll cur = 0; rep(i, 0, t) cur += a[ham[i] / n][ham[i] % n]; if (cur > best_score) { best_score = cur; best_path.assign(ham.begin(), ham.begin() + t); } rep(r, t, (int)ham.size()) { cur += a[ham[r] / n][ham[r] % n]; cur -= a[ham[r - t] / n][ham[r - t] % n]; if (cur > best_score) { best_score = cur; best_path.assign(ham.begin() + (r - t + 1), ham.begin() + (r + 1)); } } } int onward_degree(int v, const vector& used) { int x = v / n, y = v % n, deg = 0; rep(k, 0, 4) { int nx = x + DX[k], ny = y + DY[k]; if (0 <= nx && nx < n && 0 <= ny && ny < n && !used[id(nx, ny)]) deg++; } return deg; } bool weighted_dfs_from(int start, vector& path, const Timer& timer, double limit) { vector used(n * n, 0); vector> order(n * n); vector ptr(n * n, 0); path.clear(); path.reserve(t); path.push_back(start); used[start] = 1; while ((int)path.size() < t && timer.elapsed() < limit) { int v = path.back(); int x = v / n, y = v % n; if (ptr[v] == 0) { vector> cand; rep(k, 0, 4) { int nx = x + DX[k], ny = y + DY[k]; if (nx < 0 || n <= nx || ny < 0 || n <= ny) continue; int nv = id(nx, ny); if (used[nv]) continue; int deg = onward_degree(nv, used); if ((int)path.size() + 1 < t && deg == 0) continue; double w = max(1, a[nx][ny]); w *= w; w *= 1.0 / (deg + 1); w *= uniform_real_distribution(0.70, 1.30)(rng); cand.push_back({-w, nv}); } sort(cand.begin(), cand.end()); rep(i, 0, 4) order[v][i] = -1; rep(i, 0, (int)cand.size()) order[v][i] = cand[i].second; } bool advanced = false; while (ptr[v] < 4 && order[v][ptr[v]] != -1) { int nv = order[v][ptr[v]++]; if (used[nv]) continue; used[nv] = 1; path.push_back(nv); advanced = true; break; } if (advanced) continue; used[v] = 0; path.pop_back(); if (path.empty()) return false; } return (int)path.size() == t; } vector random_starts_by_score() { vector> cells; cells.reserve(n * n); rep(i, 0, n) rep(j, 0, n) cells.push_back({a[i][j], id(i, j)}); sort(cells.rbegin(), cells.rend()); int pool = min((int)cells.size(), max(20, n * n / 2)); vector starts; starts.reserve(pool); rep(i, 0, pool) starts.push_back(cells[i].second); shuffle(starts.begin(), starts.end(), rng); return starts; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n >> t; a.assign(n, vector(n)); rep(i, 0, n) rep(j, 0, n) cin >> a[i][j]; Timer timer; const double LIMIT = 1.95; ll best_score = LLONG_MIN; vector best_path; rep(vertical, 0, 2) rep(rx, 0, 2) rep(ry, 0, 2) { vector ham = snake_path(vertical, rx, ry); consider_contiguous(ham, best_score, best_path); reverse(ham.begin(), ham.end()); consider_contiguous(ham, best_score, best_path); } vector path; int iter = 0; while (timer.elapsed() < LIMIT) { auto starts = random_starts_by_score(); for (int st : starts) { if (timer.elapsed() >= LIMIT) break; if (!weighted_dfs_from(st, path, timer, LIMIT)) continue; ll sc = score_path(path); if (sc > best_score) { best_score = sc; best_path = path; } iter++; } } if (!valid_path(best_path)) { best_path.clear(); vector ham = snake_path(false, false, false); best_path.assign(ham.begin(), ham.begin() + t); } cout << t << '\n'; for (int v : best_path) cout << v / n << ' ' << v % n << '\n'; cerr << "score=" << best_score << " iter=" << iter << " time=" << timer.elapsed() << '\n'; return 0; }