#include using namespace std; const int N = 20; mt19937 rng(random_device{}()); uniform_real_distribution d01(0, 1); uniform_int_distribution rng10(0, 9); uniform_int_distribution rngN(0, 19); enum { POI_PERSON, POI_BOX, POI_TARGET, POI_EXCEED, }; #define compiler_assume(condition) \ do { \ if (!(condition)) \ __builtin_unreachable(); \ } while (false); \ struct Maze { bitset M[N]; int poi[3][2]; int at(int x, int y) const { return M[x][y]; } void set(int x, int y, int v) { M[x][y] = v; } void flip(int x, int y) { M[x].flip(y); } bool isValid() const { for (int i = 0; i < POI_EXCEED; ++i) { for (int j : {0, 1}) { if (poi[i][j] < 0 || poi[i][j] >= N) return false; } if (at(poi[i][0], poi[i][1]) == 0) return false; } return true; } }; inline Maze loadMaze(std::FILE *f) { char *s = new char[N + 5]; Maze res; for (int i = 0; i < N; ++i) { std::fscanf(f, "%s", s); for (int j = 0; j < N; ++j) { res.set(i, j, s[j] != '#'); switch (s[j]) { case 'P': res.poi[POI_PERSON][0] = i; res.poi[POI_PERSON][1] = j; break; case '*': res.poi[POI_BOX][0] = i; res.poi[POI_BOX][1] = j; break; case 'O': res.poi[POI_TARGET][0] = i; res.poi[POI_TARGET][1] = j; break; } } } delete[] s; return res; } inline void writeMaze(const Maze &m, std::FILE *f) { for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { if (i == m.poi[POI_PERSON][0] && j == m.poi[POI_PERSON][1]) std::fputc('P', f); else if (i == m.poi[POI_BOX][0] && j == m.poi[POI_BOX][1]) std::fputc('*', f); else if (i == m.poi[POI_TARGET][0] && j == m.poi[POI_TARGET][1]) std::fputc('O', f); else if (m.at(i, j) == 0) std::fputc('#', f); else std::fputc('.', f); } std::fputc('\n', f); } } namespace Solve { bitset mp[N]; int dis[N][N][N][N]; queue> qu; inline int bfs(int px, int py, int bx, int by, int tx, int ty) { memset(dis, 127, sizeof(dis)); while (!qu.empty()) qu.pop(); auto expand = [&](int npx, int npy, int nbx, int nby, int d) { if (npx < 0 || npx >= N) return; if (npy < 0 || npy >= N) return; if (nbx < 0 || nbx >= N) return; if (nby < 0 || nby >= N) return; if (mp[npx][npy] == 0) return; if (mp[nbx][nby] == 0) return; if (npx == nbx && npy == nby) return; if (dis[npx][npy][nbx][nby] <= d) return; dis[npx][npy][nbx][nby] = d; qu.emplace(npx, npy, nbx, nby); }; expand(px, py, bx, by, 0); while (!qu.empty()) { auto [npx, npy, nbx, nby] = qu.front(); int d = dis[npx][npy][nbx][nby]; qu.pop(); if (nbx == tx && nby == ty) return d; expand(npx + 1, npy, nbx, nby, d + 1); expand(npx - 1, npy, nbx, nby, d + 1); expand(npx, npy + 1, nbx, nby, d + 1); expand(npx, npy - 1, nbx, nby, d + 1); if (npx + 1 == nbx && npy == nby) expand(npx + 1, npy, nbx + 1, nby, d + 1); if (npx - 1 == nbx && npy == nby) expand(npx - 1, npy, nbx - 1, nby, d + 1); if (npx == nbx && npy + 1 == nby) expand(npx, npy + 1, nbx, nby + 1, d + 1); if (npx == nbx && npy - 1 == nby) expand(npx, npy - 1, nbx, nby - 1, d + 1); } return -1; } inline int solve(Maze q) { for (int i = 0; i < 20; ++i) mp[i] = q.M[i]; return bfs(q.poi[POI_PERSON][0], q.poi[POI_PERSON][1] , q.poi[POI_BOX][0], q.poi[POI_BOX][1], q.poi[POI_TARGET][0], q.poi[POI_TARGET][1]); } }; // namespace Solve const float initial_temperature = 10; const float initial_temperature_pv = 70; const float termperature_delta = .98; const int population_limit = 30; struct Gene { Maze m; int w; float T, eT; Gene() {} Gene(const Maze &m, int w, float T) : m(m), w(w), T(T) {} }; inline void expand(const Maze &q, std::vector &res, float t) { int s = Solve::solve(q); if (s != -1) res.emplace_back(q, s, t); } inline void expandAll(Maze q, std::vector &res, float t = initial_temperature) { for (int i = 0; i < POI_EXCEED; ++i) { for (int j : {0, 1}) { for (int d : {-1, 1}) { compiler_assume(q.poi[i][j] >= 0 && q.poi[i][j] < N); int z = q.poi[i][j] + d; if (0 <= z && z < N) { q.poi[i][j] += d; expand(q, res, t); q.poi[i][j] -= d; } } } } for (int i = 0; i < 25; i++) { compiler_assume(q.isValid()); int x = rngN(rng), y = rngN(rng); q.flip(x, y); if (q.isValid()) expand(q, res, t); q.flip(x, y); } for (int i = 0; i < 4; ++i) { Maze qq = q; for (int j = 0; j < 60; ++j) { int x = rngN(rng), y = rngN(rng); qq.set(x, y, 1); } expand(qq, res, initial_temperature_pv); } for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { int x = (rng10(rng) <= 2); if (x) q.flip(i, j); } } for (int i = 0; i < POI_EXCEED; ++i) { q.set(q.poi[i][0], q.poi[i][1], 1); } if (q.isValid()) expand(q, res, t); }; inline void writeGene(const Gene &g, std::FILE *f) { std::fprintf(f, "%d %lf\n", g.w, g.T); writeMaze(g.m, f); } inline Gene loadGene(std::FILE *f) { Gene g; std::fscanf(f, "%d %f", &g.w, &g.T); g.m = loadMaze(f); return g; } inline void writePopulation(const std::vector &p, int best_w, std::FILE *f) { std::fprintf(f, "%d %d\n", (int)p.size(), best_w); for (const auto &g : p) { writeGene(g, f); } } inline void loadPopulation(std::vector &p, int &best_w, std::FILE *f) { int pn; std::fscanf(f, "%d %d", &pn, &best_w); p.resize(pn); for (int i = 0; i < pn; ++i) { p[i] = loadGene(f); } } inline void constructMain() { std::vector population; int history_best_w; auto frecord = std::fopen("current.txt", "r"); loadPopulation(population, history_best_w, frecord); std::fclose(frecord); for (int iter_id = 1; ; ++iter_id) { std::vector nxt; for (auto &gene : population) { expandAll(gene.m, nxt, std::max(gene.T, initial_temperature)); nxt.push_back(std::move(gene)); } population.clear(); int best_w = 0, hbest_w = 0; for (const auto &gene : nxt) { best_w = std::max(best_w, gene.w); if (gene.T > initial_temperature) hbest_w = std::max(hbest_w, gene.w); } std::vector Hpopulation, Lpopulation; for (auto &gene : nxt) { const bool is_ht = gene.T > initial_temperature; const float dE = (is_ht ? hbest_w + 1 : best_w) - gene.w; gene.eT = exp(-dE / gene.T); if (gene.eT > d01(rng)) { gene.T *= termperature_delta; if (is_ht) Hpopulation.push_back(std::move(gene)); else Lpopulation.push_back(std::move(gene)); } } nxt.clear(); auto cmpET = [](const Gene &a, const Gene &b) { return a.eT > b.eT; }; if (Hpopulation.size() > population_limit) { sort(Hpopulation.begin(), Hpopulation.end(), cmpET); Hpopulation.resize(population_limit); } if (Lpopulation.size() > population_limit) { sort(Lpopulation.begin(), Lpopulation.end(), cmpET); Lpopulation.resize(population_limit); } population = std::move(Lpopulation); for (auto &g : Hpopulation) { population.push_back(std::move(g)); } Lpopulation.clear(); Hpopulation.clear(); bool broke_record = best_w > history_best_w; if (broke_record) { history_best_w = best_w; printf("New Record: %d!\n", best_w); std::FILE *f = fopen("best.txt", "w"); writePopulation(population, best_w, f); fclose(f); } if (true) { printf("Iterated for %d times.\n", iter_id); printf("Current Best=%d, History Best=%d, Population Size=%d\n" , best_w, history_best_w, (int)population.size()); std::FILE *f = fopen("current.txt", "w"); writePopulation(population, best_w, f); fclose(f); } } } int main() { constructMain(); return 0; }