// Implementation by Gemini CLI - v7_chokudai_sa.cpp #include #include #include #include #include #include #include #include using namespace std; int N, T; int A[20][20]; uint64_t zobrist[400]; int dr[] = {0, 0, 1, -1}; int dc[] = {1, -1, 0, 0}; struct State { int r, c; long long score; int parent_idx; int step; bitset<400> visited; uint64_t hash; double eval_score; bool operator<(const State& other) const { return eval_score < other.eval_score; } }; struct SavedState { int r, c; int parent_idx; }; vector> run_chokudai(auto start_time, double time_limit) { priority_queue queues[405]; vector history[405]; unordered_map seen[405]; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { State s; s.r = i; s.c = j; s.score = A[i][j]; s.parent_idx = -1; s.step = 1; s.visited.set(i * N + j); s.hash = zobrist[i * N + j]; int deg = 0; for(int d=0; d<4; d++){ int nr=i+dr[d], nc=j+dc[d]; if(nr>=0 && nr=0 && nc(chrono::steady_clock::now() - start_time).count() > time_limit) break; } bool pushed = false; for (int t = 1; t <= T; t++) { if (queues[t].empty()) continue; State s = queues[t].top(); queues[t].pop(); uint64_t state_key = s.hash ^ (uint64_t(s.r * N + s.c) << 32); if (seen[t].count(state_key) && seen[t][state_key] >= s.score) continue; seen[t][state_key] = s.score; int current_idx = history[t].size(); history[t].push_back({s.r, s.c, s.parent_idx}); if (s.score > best_total_score) { best_total_score = s.score; best_total_step = t; best_total_idx = current_idx; } if (t >= T) continue; for (int d = 0; d < 4; d++) { int nr = s.r + dr[d], nc = s.c + dc[d]; if (nr >= 0 && nr < N && nc >= 0 && nc < N && !s.visited.test(nr * N + nc)) { State next_s; next_s.r = nr; next_s.c = nc; next_s.score = s.score + A[nr][nc]; next_s.parent_idx = current_idx; next_s.step = t + 1; next_s.visited = s.visited; next_s.visited.set(nr * N + nc); next_s.hash = s.hash ^ zobrist[nr * N + nc]; int deg = 0; for(int d2=0; d2<4; d2++){ int nnr=nr+dr[d2], nnc=nc+dc[d2]; if(nnr>=0 && nnr=0 && nnc> path; int curr_t = best_total_step, curr_idx = best_total_idx; while (curr_t >= 1) { path.push_back({history[curr_t][curr_idx].r, history[curr_t][curr_idx].c}); curr_idx = history[curr_t][curr_idx].parent_idx; curr_t--; } reverse(path.begin(), path.end()); return path; } int main() { auto start_time = chrono::steady_clock::now(); mt19937 rng(42); mt19937_64 rng64(42); for (int i = 0; i < 400; i++) zobrist[i] = rng64(); if (!(cin >> N >> T)) return 0; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) cin >> A[i][j]; vector> best_path = run_chokudai(start_time, 1.2); long long best_score = 0; for (auto p : best_path) best_score += A[p.first][p.second]; // Local Search: Backtrack and regrow while (chrono::duration(chrono::steady_clock::now() - start_time).count() < 1.85) { int k = uniform_int_distribution(0, (int)best_path.size() - 2)(rng); vector> current_path; bitset<400> visited; long long current_score = 0; for (int i = 0; i <= k; i++) { current_path.push_back(best_path[i]); visited.set(best_path[i].first * N + best_path[i].second); current_score += A[best_path[i].first][best_path[i].second]; } // Regrow greedily with some randomness while (current_path.size() < T) { int r = current_path.back().first, c = current_path.back().second; vector> candidates; for (int d = 0; d < 4; d++) { int nr = r + dr[d], nc = c + dc[d]; if (nr >= 0 && nr < N && nc >= 0 && nc < N && !visited.test(nr * N + nc)) { candidates.push_back({nr, nc}); } } if (candidates.empty()) break; // Pick candidate: higher value is better, but add some randomness sort(candidates.begin(), candidates.end(), [](const pair& a, const pair& b) { return A[a.first][a.second] > A[b.first][b.second]; }); int idx = 0; if (candidates.size() > 1 && uniform_real_distribution(0, 1)(rng) < 0.2) { idx = uniform_int_distribution(0, candidates.size() - 1)(rng); } int nr = candidates[idx].first, nc = candidates[idx].second; current_path.push_back({nr, nc}); visited.set(nr * N + nc); current_score += A[nr][nc]; } if (current_score > best_score) { best_score = current_score; best_path = current_path; } } cout << (int)best_path.size() << endl; for (auto p : best_path) cout << p.first << " " << p.second << endl; return 0; }