A - Ward the Village 解説 by buch

基本文法で解く解き方

考え方

  • 怪異の場所は気にせずになるべく多くのマスにお札を置く
  • 何回もシミュレーションして一番良かった手順を出力する

方法

  • 移動できる3つの手はいろいろなところに移動できるように奇数と偶数を混ぜる

1000回繰り返す

  • x, y, visited を初期化する
  • 初めの3手は固定する
  • 4手目以降は毎回ランダムに選び、まだお札を立てていなければその場所に行く。すでに立ててあればランダムにもう一度引く
  • ただし10回行ってもお札のない場所がなければ、お札がすでに立ててあっても選ぶ(3つどこに行ってもすでにお札が置いてあることもあるから)
  • 怪異の場所から危険度を計算し、x, y, visited を更新する
  • 10000手まで終わり、もし今の最善の危険度よりも危険度が低かったら採用する

コード

#include <bits/stdc++.h> 

using namespace std;
using ll = long long;
#define rep(i, n) for (ll i = 0; i < (ll)(n); i++) 

struct Init {
    Init() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        cout << fixed << setprecision(15);
    }
} init;

int main() {
    ll N, M;
    cin >> N >> M;
    vector<ll> A(10000);
    vector<ll> B(10000);
    rep(i, 10000) {
        cin >> A[i] >> B[i];
    }
    
    vector<ll> i_vec = {29, 17, 63};
    vector<ll> j_vec = {60, 23, 71};
    ll best = (1LL << 60);
    vector<int> step(10000);   // 今試している手順
    vector<int> answer(10000); // 一番良い手順

    rep(idx, 1000) {
        ll x = 0;
        ll y = 0;
        ll denger = 0;
        vector<vector<int>> visited(100, vector<int>(100, 0));

        rep(turn, 10000) {
            if (turn == 0) {
                step[turn] = 2;
                x = (x + i_vec[2]) % 100;
                y = (y + j_vec[2]) % 100;
                visited[y][x] = 1;
                denger += abs(x - A[turn]) + abs(y - B[turn]);
                continue;
            }
            if (turn == 1) {
                step[turn] = 2;
                x = (x + i_vec[2]) % 100;
                y = (y + j_vec[2]) % 100;
                visited[y][x] = 1;
                denger += abs(x - A[turn]) + abs(y - B[turn]);
                continue;
            }
            if (turn == 2) {
                step[turn] = 0;
                x = (x + i_vec[0]) % 100;
                y = (y + j_vec[0]) % 100;
                visited[y][x] = 1;
                denger += abs(x - A[turn]) + abs(y - B[turn]);
                continue;
            }

            ll bad = 0; // 選んだマスにすでにお札があった回数
            while (true) {
                ll r = rand() % 3;
                ll next_x = (x + i_vec[r]) % 100;
                ll next_y = (y + j_vec[r]) % 100;
                if (!visited[next_y][next_x] || bad >= 10) {
                    x = next_x;
                    y = next_y;
                    step[turn] = r;
                    break;
                } else {
                    bad++;
                }
            }
            denger += abs(x - A[turn]) + abs(y - B[turn]);
            visited[y][x] = 1;
        }

        if (best > denger) {
            best = denger;
            answer = step;
        }
    }

    rep(idx, 3) {
        cout << i_vec[idx] << ' ' << j_vec[idx] << '\n';
    }
    rep(idx, 10000) {
        cout << answer[idx] << '\n';
    }
}

このコードを実行すると、実行時間は581ms,スコアは 391,093,468点 になります

ループ回数を1,000回にしましたが、制限時間ギリギリまでループを回せるように調整すればもう少しスコアを上げられると思います