Submission #47959319
Source Code Expand
// 焼きなまし法のサンプルコード // VSCODEの拡張機能C/C++の設定 // "C_Cpp.clang_format_sortIncludes": true, // "C_Cpp.clang_format_style": "Google" でフォーマット済み #include <algorithm> //shuffleに必要 #include <array> #include <chrono> // 時間計測 #include <iostream> //入出力 #include <random> #include <vector> using namespace std; // 自己責任で使おう // 時間をDouble型で管理し、経過時間も取り出せるクラス class TimeKeeperDouble { private: std::chrono::high_resolution_clock::time_point start_time_; double time_threshold_; double now_time_ = 0; public: // 時間制限をミリ秒単位で指定してインスタンスをつくる。 TimeKeeperDouble(const double time_threshold) : start_time_(std::chrono::high_resolution_clock::now()), time_threshold_(time_threshold) {} // 経過時間をnow_time_に格納する。 void setNowTime() { auto diff = std::chrono::high_resolution_clock::now() - this->start_time_; this->now_time_ = std::chrono::duration_cast<std::chrono::microseconds>(diff).count() * 1e-3; // ms } // 経過時間をnow_time_に取得する。 double getNowTime() const { return this->now_time_; } // インスタンス生成した時から指定した時間制限を超過したか判定する。 bool isTimeOver() const { return now_time_ >= time_threshold_; } }; // 乱数を生成するクラス class Random { public: std::mt19937 mt_; // シード0でメルセンヌツイスターの乱数生成器を初期化 // 0以上1.0未満の実数の範囲の乱数生成 uniform_real_distribution<double> dd_{0, 1.0}; // seedを指定して初期化 Random(const int seed = 0) : mt_(std::mt19937(seed)) {} // 0以上m未満の整数の範囲の乱数 inline int nextInt(const int m) { uniform_int_distribution<int> di(0, m - 1); return di(mt_); } // 0以上1.0未満の実数の範囲の乱数 inline double nextDouble() { return dd_(mt_); } // 0以上1.0未満の実数の範囲の乱数のlog。焼きなまし法で使いやすい。 inline double nextLog() { return log(dd_(mt_)); } }; Random rnd{}; constexpr int H = 50; // 盤面の縦方向の大きさ constexpr int W = 50; // 盤面の横方向の大きさ constexpr int TILE_N = 2500; // タイルの総数。2500枚よりは絶対少ない int first_coord; // 高橋くんの初期位置:(y,x)はy*W+xとして表現する int tiles[H * W]; // tiles[coord1]とtiles[coord2]が同じなら同じタイル int points[H * W]; // tiles[coord]を訪れた場合の得点 vector<vector<int>> next_coords{H * W}; // next_coords[coord]=coordの隣のリスト // 始点を指定して探索するクラス class DfsSolver { public: // visiteds_[tile]はtileが踏まれたかどうかを表す array<bool, TILE_N> visiteds_; vector<int> path_; // coordのリスト。今探索中のパス vector<int> best_path_; // coordのリスト。今のところ一番いいパス int best_score_ = 0; // 今のところ一番いいスコア int score_ = 0; // 今探索中のパスのスコア int remaining_search_cnt_; // 残り探索回数 // dfsを開始する。dfsは再帰で実装するので開始する関数が必要 void start() { best_path_.clear(); score_ = 0; visiteds_.fill(0); path_.clear(); remaining_search_cnt_ = 4000; dfs(first_coord); } private: // coordを始点とした探索 void dfs(const int coord) { path_.emplace_back(coord); score_ += points[coord]; visiteds_[tiles[coord]] = 1; if (best_score_ < score_) { best_score_ = score_; best_path_ = path_; } remaining_search_cnt_--; if (remaining_search_cnt_ == 0) return; vector<int> legal_next_coords; for (const auto &next_coord : next_coords[coord]) { if (!visiteds_[tiles[next_coord]]) { legal_next_coords.emplace_back(next_coord); } } for (const auto &next_coord : legal_next_coords) { if (!visiteds_[tiles[next_coord]]) { dfs(next_coord); if (remaining_search_cnt_ == 0) return; } } path_.pop_back(); score_ -= points[coord]; visiteds_[tiles[coord]] = 0; } }; // 始点と終点を指定して問題の一部を探索するクラス // 近傍計算に使う class DfsPartSolver { public: // visiteds_[tile]はtileが踏まれたかどうかを表す array<bool, TILE_N> visiteds_; vector<int> path_; // coordのリスト。今探索中のパス vector<int> best_path_; // coordのリスト。今のところ一番いいパス int best_score_ = 0; // 今のところ一番いいスコア int score_ = 0; // 今探索中のパスのスコア int remaining_search_cnt_; // 残り探索回数 int goal_ = 0; // 目標地点 // dfsを開始する。dfsは再帰で実装するので開始する関数が必要 // DfsSolverのstartとは以下の点で異なる // 始点、終点を指定する // 注目しない部分はタイルが踏まれているものとして初期化する void start(const int start, const int goal, const array<bool, TILE_N> &visiteds, const int remaining_search_cnt) { goal_ = goal; best_path_.clear(); best_score_ = 0; score_ = 0; visiteds_ = visiteds; path_.clear(); remaining_search_cnt_ = remaining_search_cnt; dfs(start); } // coordを始点としてgoal_を終点とした探索 void dfs(const int coord) { if (!visiteds_[tiles[coord]]) { path_.emplace_back(coord); score_ += points[coord]; visiteds_[tiles[coord]] = 1; } remaining_search_cnt_--; if (remaining_search_cnt_ == 0) return; if (coord != goal_) { vector<int> legal_next_coords; for (const auto &next_coord : next_coords[coord]) { if (!visiteds_[tiles[next_coord]]) { legal_next_coords.emplace_back(next_coord); } else if (next_coord == goal_) { // goalにたどり着いた時点で探索を終了 best_score_ = score_; best_path_ = path_; remaining_search_cnt_ = 0; return; } } shuffle(legal_next_coords.begin(), legal_next_coords.end(), rnd.mt_); for (const auto &next_coord : legal_next_coords) { if (!visiteds_[tiles[next_coord]]) { dfs(next_coord); if (remaining_search_cnt_ == 0) return; } } } path_.pop_back(); score_ -= points[coord]; visiteds_[tiles[coord]] = 0; } }; struct State { vector<int> now_path_; // coordのリスト。今までに遷移してきた現在のパス // 初期解を生成する void solve_first() { DfsSolver dfs_solver{}; dfs_solver.start(); this->now_path_ = dfs_solver.path_; } void simulatedAnnealingWithTimeThreshold(const int64_t time_threshold, const double start_temp, const double end_temp) { /***************************************************************/ // 1. 時間計測開始 auto time_keeper = TimeKeeperDouble(time_threshold); // end of 1 /***************************************************************/ /***************************************************************/ // 2. 初期解を生成し、ベストスコアを更新する。 // ループ前に必要な変数があれば用意する。 // 初期解生成が重すぎる場合は時間計測を分ける必要あり。 this->solve_first(); array<bool, TILE_N> now_visiteds{}; DfsPartSolver dfs_part_solver{}; for (int i = 0; i < now_path_.size(); i++) { now_visiteds[tiles[now_path_[i]]] = 1; } // end of 2 /***************************************************************/ for (;;) { /*************************************************************/ // 3. 制限時間の確認 time_keeper.setNowTime(); if (time_keeper.isTimeOver()) { break; } // end of 3 /*************************************************************/ /*************************************************************/ // 4. 焼きなましの温度と遷移の閾値を決定する。 // 以下の例ではstart_tempから経過時間に応じて線形にend_tempに冷却する。 // 冷却スケジュールは線形である必要はない。 double temp = start_temp + (end_temp - start_temp) * (time_keeper.getNowTime() / time_threshold); // end of 4 /************************************************************/ /*************************************************************/ // 5. 近傍から1つ選んでスコアを計算 // 今のパスの中で壊す部分パスの長さ。 // コンテスト1位のatsさんは徐々に狭くしていたが、しなくてもいいスコアになる。 int delete_path_length = rnd.nextInt((int)(1 + now_path_.size() * 0.05)); int start_path_id = rnd.nextInt(now_path_.size() - delete_path_length); int end_path_id = start_path_id + delete_path_length; auto next_visiteds = now_visiteds; // 探索するパスの長さよりちょっと多いぐらいの探索をする int remaining_search_cnt = 4 * delete_path_length; // start_path_idとend_path_idの間のパスを削り、 // 削った部分のスコアをメモする int now_score = 0; for (int i = start_path_id + 1; i <= end_path_id - 1; i++) { now_score += points[now_path_[i]]; next_visiteds[tiles[now_path_[i]]] = 0; } dfs_part_solver.start(now_path_[start_path_id], now_path_[end_path_id], next_visiteds, remaining_search_cnt); // end of 5 /*************************************************************/ /*************************************************************/ // 6. 試した近傍を反映させる(遷移する) // ※実装方針次第では5の段階で反映させ、 // 6では遷移させたくない場合に元の状態に戻す方針もできる int next_score = dfs_part_solver.best_score_; int diff = next_score - now_score; if (dfs_part_solver.best_path_.size() > 0 && exp(diff / temp) > rnd.nextDouble()) { // visitedsの更新 now_visiteds = dfs_part_solver.visiteds_; // 今回生成した部分パスを挿入した全体パスを生成 vector<int> transitioned_path; for (int i = 0; i <= start_path_id; i++) { transitioned_path.emplace_back(now_path_[i]); } for (int i = 0; i < dfs_part_solver.best_path_.size(); i++) { transitioned_path.emplace_back(dfs_part_solver.best_path_[i]); } for (int i = end_path_id; i < now_path_.size(); i++) { transitioned_path.emplace_back(now_path_[i]); } now_path_ = transitioned_path; } // end of 6 /*************************************************************/ } } // 入力を受け取る void input() { int sy, sx; cin >> sy >> sx; first_coord = sy * W + sx; for (int y = 0; y < H; y++) { for (int x = 0; x < W; x++) { cin >> tiles[y * W + x]; } } for (int y = 0; y < H; y++) { for (int x = 0; x < W; x++) { cin >> points[y * W + x]; } } } // 解を出力する void output() { // now_pathは座標のリストになっているため、移動方向に直して出力する string res; for (int i = 0; i < now_path_.size() - 1; i++) { int diff = now_path_[i + 1] - now_path_[i]; if (diff == 1) { res.push_back('R'); } else if (diff == -1) { res.push_back('L'); } else if (diff == W) { res.push_back('D'); } else if (diff == -W) { res.push_back('U'); } } cout << res << endl; } }; void initialize_global_info() { // 各座標から行ける場所(盤面の枠内かつタイルが異なる)をメモしておく const int dy[4] = {1, 0, -1, 0}; const int dx[4] = {0, 1, 0, -1}; for (int y = 0; y < H; y++) { for (int x = 0; x < W; x++) { int coord = y * W + x; for (int d = 0; d < 4; d++) { int ty = y + dy[d]; int tx = x + dx[d]; if (ty >= 0 && ty < H && tx >= 0 && tx < W) { int tcoord = ty * W + tx; if (tiles[coord] != tiles[tcoord]) { next_coords[coord].emplace_back(tcoord); } } } } } } int main() { State state; // 入力を受け取る state.input(); // 前計算などの準備 initialize_global_info(); // 焼きなまし法で解を求める state.simulatedAnnealingWithTimeThreshold(/*time_threshold*/ 1950, /*start_temp*/ 150, /*end_temp*/ 0); // 解を出力する state.output(); return 0; }
Submission Info
Submission Time | |
---|---|
Task | A - Walking on Tiles |
User | thunder |
Language | C++ 20 (gcc 12.2) |
Score | 6283426 |
Code Size | 13598 Byte |
Status | AC |
Exec Time | 1952 ms |
Memory | 4248 KiB |
Compile Error
Main.cpp: In member function ‘void State::simulatedAnnealingWithTimeThreshold(int64_t, double, double)’: Main.cpp:224:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<int>::size_type’ {aka ‘long unsigned int’} [-Wsign-compare] 224 | for (int i = 0; i < now_path_.size(); i++) { | ~~^~~~~~~~~~~~~~~~~~ Main.cpp:292:27: warning: comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<int>::size_type’ {aka ‘long unsigned int’} [-Wsign-compare] 292 | for (int i = 0; i < dfs_part_solver.best_path_.size(); i++) { | ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Main.cpp:295:37: warning: comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<int>::size_type’ {aka ‘long unsigned int’} [-Wsign-compare] 295 | for (int i = end_path_id; i < now_path_.size(); i++) { | ~~^~~~~~~~~~~~~~~~~~ Main.cpp: In member function ‘void State::output()’: Main.cpp:326:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<int>::size_type’ {aka ‘long unsigned int’} [-Wsign-compare] 326 | for (int i = 0; i < now_path_.size() - 1; i++) { | ~~^~~~~~~~~~~~~~~~~~~~~~
Judge Result
Set Name | test_ALL | ||
---|---|---|---|
Score / Max Score | 6283426 / 25000000 | ||
Status |
|
Set Name | Test Cases |
---|---|
test_ALL | test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt, test_0050.txt, test_0051.txt, test_0052.txt, test_0053.txt, test_0054.txt, test_0055.txt, test_0056.txt, test_0057.txt, test_0058.txt, test_0059.txt, test_0060.txt, test_0061.txt, test_0062.txt, test_0063.txt, test_0064.txt, test_0065.txt, test_0066.txt, test_0067.txt, test_0068.txt, test_0069.txt, test_0070.txt, test_0071.txt, test_0072.txt, test_0073.txt, test_0074.txt, test_0075.txt, test_0076.txt, test_0077.txt, test_0078.txt, test_0079.txt, test_0080.txt, test_0081.txt, test_0082.txt, test_0083.txt, test_0084.txt, test_0085.txt, test_0086.txt, test_0087.txt, test_0088.txt, test_0089.txt, test_0090.txt, test_0091.txt, test_0092.txt, test_0093.txt, test_0094.txt, test_0095.txt, test_0096.txt, test_0097.txt, test_0098.txt, test_0099.txt |
Case Name | Status | Exec Time | Memory |
---|---|---|---|
test_0000.txt | AC | 1952 ms | 4108 KiB |
test_0001.txt | AC | 1952 ms | 3976 KiB |
test_0002.txt | AC | 1952 ms | 4204 KiB |
test_0003.txt | AC | 1952 ms | 4196 KiB |
test_0004.txt | AC | 1952 ms | 4176 KiB |
test_0005.txt | AC | 1952 ms | 4220 KiB |
test_0006.txt | AC | 1952 ms | 4084 KiB |
test_0007.txt | AC | 1952 ms | 4096 KiB |
test_0008.txt | AC | 1952 ms | 4152 KiB |
test_0009.txt | AC | 1952 ms | 4228 KiB |
test_0010.txt | AC | 1952 ms | 4228 KiB |
test_0011.txt | AC | 1952 ms | 4096 KiB |
test_0012.txt | AC | 1952 ms | 4120 KiB |
test_0013.txt | AC | 1952 ms | 4080 KiB |
test_0014.txt | AC | 1952 ms | 4116 KiB |
test_0015.txt | AC | 1952 ms | 4124 KiB |
test_0016.txt | AC | 1952 ms | 4068 KiB |
test_0017.txt | AC | 1952 ms | 4108 KiB |
test_0018.txt | AC | 1952 ms | 4072 KiB |
test_0019.txt | AC | 1952 ms | 4248 KiB |
test_0020.txt | AC | 1952 ms | 4068 KiB |
test_0021.txt | AC | 1952 ms | 4084 KiB |
test_0022.txt | AC | 1952 ms | 4064 KiB |
test_0023.txt | AC | 1952 ms | 4204 KiB |
test_0024.txt | AC | 1952 ms | 4024 KiB |
test_0025.txt | AC | 1952 ms | 4160 KiB |
test_0026.txt | AC | 1952 ms | 4172 KiB |
test_0027.txt | AC | 1952 ms | 4096 KiB |
test_0028.txt | AC | 1952 ms | 4052 KiB |
test_0029.txt | AC | 1952 ms | 4104 KiB |
test_0030.txt | AC | 1952 ms | 4160 KiB |
test_0031.txt | AC | 1952 ms | 4128 KiB |
test_0032.txt | AC | 1952 ms | 4032 KiB |
test_0033.txt | AC | 1952 ms | 4088 KiB |
test_0034.txt | AC | 1952 ms | 4080 KiB |
test_0035.txt | AC | 1952 ms | 4100 KiB |
test_0036.txt | AC | 1952 ms | 4044 KiB |
test_0037.txt | AC | 1952 ms | 4224 KiB |
test_0038.txt | AC | 1952 ms | 4168 KiB |
test_0039.txt | AC | 1952 ms | 4080 KiB |
test_0040.txt | AC | 1952 ms | 4104 KiB |
test_0041.txt | AC | 1952 ms | 4060 KiB |
test_0042.txt | AC | 1952 ms | 4056 KiB |
test_0043.txt | AC | 1952 ms | 4068 KiB |
test_0044.txt | AC | 1952 ms | 4092 KiB |
test_0045.txt | AC | 1952 ms | 4020 KiB |
test_0046.txt | AC | 1952 ms | 4128 KiB |
test_0047.txt | AC | 1952 ms | 4064 KiB |
test_0048.txt | AC | 1952 ms | 4092 KiB |
test_0049.txt | AC | 1952 ms | 4244 KiB |
test_0050.txt | AC | 1952 ms | 4132 KiB |
test_0051.txt | AC | 1952 ms | 4064 KiB |
test_0052.txt | AC | 1952 ms | 4084 KiB |
test_0053.txt | AC | 1952 ms | 4036 KiB |
test_0054.txt | AC | 1952 ms | 4032 KiB |
test_0055.txt | AC | 1952 ms | 4076 KiB |
test_0056.txt | AC | 1952 ms | 4144 KiB |
test_0057.txt | AC | 1952 ms | 4168 KiB |
test_0058.txt | AC | 1952 ms | 4072 KiB |
test_0059.txt | AC | 1952 ms | 4108 KiB |
test_0060.txt | AC | 1952 ms | 4044 KiB |
test_0061.txt | AC | 1952 ms | 4096 KiB |
test_0062.txt | AC | 1952 ms | 4080 KiB |
test_0063.txt | AC | 1952 ms | 4128 KiB |
test_0064.txt | AC | 1952 ms | 4084 KiB |
test_0065.txt | AC | 1952 ms | 4248 KiB |
test_0066.txt | AC | 1952 ms | 4244 KiB |
test_0067.txt | AC | 1952 ms | 4060 KiB |
test_0068.txt | AC | 1952 ms | 4116 KiB |
test_0069.txt | AC | 1952 ms | 4064 KiB |
test_0070.txt | AC | 1952 ms | 4036 KiB |
test_0071.txt | AC | 1952 ms | 4084 KiB |
test_0072.txt | AC | 1952 ms | 3948 KiB |
test_0073.txt | AC | 1952 ms | 4024 KiB |
test_0074.txt | AC | 1952 ms | 4120 KiB |
test_0075.txt | AC | 1952 ms | 4056 KiB |
test_0076.txt | AC | 1952 ms | 4104 KiB |
test_0077.txt | AC | 1952 ms | 4084 KiB |
test_0078.txt | AC | 1952 ms | 4100 KiB |
test_0079.txt | AC | 1952 ms | 4124 KiB |
test_0080.txt | AC | 1952 ms | 4052 KiB |
test_0081.txt | AC | 1952 ms | 4088 KiB |
test_0082.txt | AC | 1952 ms | 4096 KiB |
test_0083.txt | AC | 1952 ms | 4096 KiB |
test_0084.txt | AC | 1952 ms | 4092 KiB |
test_0085.txt | AC | 1952 ms | 4056 KiB |
test_0086.txt | AC | 1952 ms | 4148 KiB |
test_0087.txt | AC | 1952 ms | 4136 KiB |
test_0088.txt | AC | 1952 ms | 4084 KiB |
test_0089.txt | AC | 1952 ms | 3928 KiB |
test_0090.txt | AC | 1952 ms | 4112 KiB |
test_0091.txt | AC | 1952 ms | 3984 KiB |
test_0092.txt | AC | 1952 ms | 4212 KiB |
test_0093.txt | AC | 1952 ms | 4144 KiB |
test_0094.txt | AC | 1952 ms | 4028 KiB |
test_0095.txt | AC | 1952 ms | 4164 KiB |
test_0096.txt | AC | 1952 ms | 4188 KiB |
test_0097.txt | AC | 1952 ms | 4076 KiB |
test_0098.txt | AC | 1952 ms | 4056 KiB |
test_0099.txt | AC | 1952 ms | 4116 KiB |