данный код не проходит time limit в 2 секунды. Ско...
Created on: May 19, 2025
Created on: May 19, 2025
данный код не проходит time limit в 2 секунды. Скорее всего здесь есть какая-то ошибка в реализации алгоритма. Помоги ее найти
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <unordered_map>
#include <climits>
#include <string>
using NextStrings = std::vector<std::pair<std::string, char>>;
#pragma pack(push, 1)
struct Gambit {
int neigh;
char dx;
Gambit(int n, char d) : neigh(n), dx(d) {
}
};
#pragma pack(pop)
class Graph {
private:
std::vector<std::vector<Gambit>> adj_;
static constexpr int kPermNum = 362880;
std::unordered_map<int, std::string> ind_state_;
std::unordered_map<std::string, int> state_ind_;
std::string asc_ = "012345678";
std::string target_ = "123456780";
std::string init_;
NextStrings DerivePossiblePermutations(const std::string& state) {
size_t zero_ind = state.find('0');
NextStrings next_strings;
if (zero_ind / 3 != 0) {
std::string next_state = state;
std::swap(next_state[zero_ind], next_state[zero_ind - 3]);
next_strings.emplace_back(next_state, 'U');
}
if (zero_ind / 3 != 2) {
std::string next_state = state;
std::swap(next_state[zero_ind], next_state[zero_ind + 3]);
next_strings.emplace_back(next_state, 'D');
}
if (zero_ind % 3 != 2) {
std::string next_state = state;
std::swap(next_state[zero_ind], next_state[zero_ind + 1]);
next_strings.emplace_back(next_state, 'R');
}
if (zero_ind % 3 != 0) {
std::string next_state = state;
std::swap(next_state[zero_ind], next_state[zero_ind - 1]);
next_strings.emplace_back(next_state, 'L');
}
return next_strings;
}
void BFS(std::vector<int>& dist, std::vector<int>& parent, std::vector<char>& parent_move) {
int init_ind = state_ind_.at(init_);
int target_ind = state_ind_.at(target_);
dist[init_ind] = 0;
std::queue<int> q;
q.emplace(init_ind);
while (!q.empty()) {
int v = q.front();
q.pop();
if (v == target_ind) {
break;
}
const std::string& cur_state = ind_state_.at(v);
NextStrings next_strings = DerivePossiblePermutations(cur_state);
for (const auto& next : next_strings) {
int neigh = state_ind_.at(next.first);
if (dist[neigh] == INT_MAX) {
dist[neigh] = dist[v] + 1;
parent[neigh] = v;
parent_move[neigh] = next.second;
q.emplace(neigh);
}
}
}
}
public:
explicit Graph(std::string s) : adj_(kPermNum), init_(std::move(s)) {
int ind = 0;
do {
state_ind_[asc_] = ind;
ind_state_[ind] = asc_;
++ind;
} while (std::next_permutation(asc_.begin(), asc_.end()));
}
void FindSolution() {
std::vector<int> dist(kPermNum, INT_MAX);
std::vector<int> parent(kPermNum, -1);
std::vector<char> parent_move(kPermNum, 0);
BFS(dist, parent, parent_move);
int target_ind = state_ind_.at(target_);
if (dist[target_ind] == INT_MAX) {
std::cout << -1;
return;
}
std::string cmd_seq;
for (int cur = target_ind; parent[cur] != -1; cur = parent[cur]) {
cmd_seq += parent_move[cur];
}
std::reverse(cmd_seq.begin(), cmd_seq.end());
std::cout << dist[target_ind] << '\n' << cmd_seq;
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
std::string init;
for (int i = 0; i < 9; ++i) {
char c = 0;
std::cin >> c;
init += c;
}
Graph graph(init);
graph.FindSolution();
}
Конструктор Graph
cppdo { state_ind_[asc_] = ind; ind_state_[ind] = asc_; } while (std::next_permutation(asc_.begin(), asc_.end()));
std::next_permutation
.unordered_map
со строковым ключом (хэш-функция + динамическое выделение памяти для std::string
).Поиск в картах во время BFS
Внутри цикла:
cppint neigh = state_ind_.at(next.first); // hash(string) каждую итерацию const std::string& cur_state = ind_state_.at(v);
BFS для самой «дальней» начальной позиции посещает ≈ 180 000 вершин, т.е. ещё сотни тысяч обращений к тем же unordered_map + копирование строк-состояний.
adj_
не используется, но инициализируется
std::vector<std::vector<Gambit>> adj_(kPermNum)
создает 362 880 пустых std::vector
-ов — лишние ~9 МБ памяти и время на конструкторы.
unordered_map
и строкПредставим перестановку как число от 0 до 362 879 при помощи кодов Лемера (Cantor).
cppconstexpr int fact[10] = {1,1,2,6,24,120,720,5040,40320,362880}; int encode(const std::array<char,9>& a) { // O(9^2) ≈ 81 операций int res = 0; bool used[9] = {}; for (int i = 0; i < 9; ++i) { int cnt = 0; for (int d = 0; d < a[i]-'0'; ++d) if (!used[d]) ++cnt; res += cnt * fact[8-i]; used[a[i]-'0'] = true; } return res; }
encode
не аллоцирует память, работает на массиве/строке фиксированной длины.decode
) потребуется лишь для восстановления пути, не для BFS.cppstd::array<int, 362880> dist; std::array<int, 362880> parent; std::array<char,362880> parent_move;
Нулевые инциализации memset
-ом ≈ 3 МБ.
Удаляем adj_
, ind_state_
, state_ind_
, Gambit
.
Даже односторонний BFS с числами проходит < 0.1 с, но если хочется ещё запас — запустите BFS одновременно от старта и цели.
cpp#include <bits/stdc++.h> using namespace std; constexpr int N = 362880; constexpr int fact[10] = {1,1,2,6,24,120,720,5040,40320,362880}; int encode(const array<char,9>& s) { /* как выше */ } array<char,9> decode(int code) { /* обратная операция */ } array<int,4> dx = { -3, +3, +1, -1 }; // U, D, R, L array<char,4> mv = { 'U','D','R','L' }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); array<char,9> start{}; for (char &c : start) cin >> c; array<char,9> goal = {'1','2','3','4','5','6','7','8','0'}; int sID = encode(start), gID = encode(goal); static int dist[N]; fill(begin(dist), end(dist), -1); static int par[N]; static char pMove[N]; queue<int> q; q.push(sID); dist[sID] = 0; while (!q.empty()) { int v = q.front(); q.pop(); if (v == gID) break; auto state = decode(v); int z = find(begin(state), end(state), '0') - begin(state); for (int k = 0; k < 4; ++k) { int nz = z + dx[k]; if (nz < 0 || nz >= 9 || (z%3==0&&k==3) || (z%3==2&&k==2)) continue; swap(state[z], state[nz]); int to = encode(state); if (dist[to] == -1) { dist[to] = dist[v]+1; par[to] = v; pMove[to]= mv[k]; q.push(to); } swap(state[z], state[nz]); // вернуть обратно } } if (dist[gID] == -1) { cout << -1; return 0; } string path; for (int cur = gID; cur != sID; cur = par[cur]) path += pMove[cur]; reverse(path.begin(), path.end()); cout << dist[gID] << '\n' << path << '\n'; }
- никаких unordered_map
, ни одной аллокации во время работы – типичная реализация проходит < 0.05 с в g++-O2 и уверенно укладывается в лимит 2 с.
Проблема | Почему медленно | Как исправить |
---|---|---|
Полный перебор next_permutation + две карты unordered_map<string, …> | Сотни тысяч хэширований и аллокаций строк | Кантор-кодирование → один int |
Доступ к картам на каждом шаге BFS | Ещё сотни тысяч хэширований | Работаем только с int |
Гигантский (и неиспользуемый) adj_ | Лишняя инициализация 362 880 векторов | Удалить |
Исправив эти места, программа легко проходит TL.