Last active
August 29, 2015 14:03
-
-
Save lnicola/9f8e14fb3d04549d3af5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 3 | |
1 1 1 1 1 1 1 1 1 1 1 1 1 | |
2 2 2 2 2 2 2 2 2 2 2 2 2 | |
6 4 | |
4 2 4 3 1 1 | |
1 1 1 1 1 1 | |
4 3 | |
2 2 1 3 | |
3 3 3 3 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _NDEBUG | |
#define _CRTDBG_MAP_ALLOC | |
#include <stdlib.h> | |
#include <crtdbg.h> | |
#endif | |
#include <algorithm> | |
#include <chrono> | |
#include <fstream> | |
#include <functional> | |
#include <iostream> | |
#include <tuple> | |
#include <unordered_set> | |
#include <vector> | |
struct configuration | |
{ | |
std::vector<std::vector<int>> pegs; | |
std::tuple<size_t, size_t> move; | |
size_t age; | |
configuration() | |
{ | |
} | |
configuration(const configuration &other) | |
: pegs(other.pegs), move(other.move), age(other.age) | |
{ | |
} | |
configuration(configuration &&other) | |
: pegs(std::move(other.pegs)), move(std::move(other.move)), age(std::move(other.age)) | |
{ | |
} | |
configuration & operator=(const configuration &other) | |
{ | |
pegs = other.pegs; | |
move = other.move; | |
age = other.age; | |
return *this; | |
} | |
configuration & operator=(configuration &&other) | |
{ | |
pegs = std::move(other.pegs); | |
move = std::move(other.move); | |
age = std::move(other.age); | |
return *this; | |
} | |
}; | |
bool operator==(const configuration &s1, const configuration &s2) | |
{ | |
return s1.pegs == s2.pegs; | |
} | |
bool operator!=(const configuration &s1, const configuration &s2) | |
{ | |
return s1.pegs != s2.pegs; | |
} | |
struct node | |
{ | |
configuration state; | |
size_t cost; | |
node(const configuration &state) | |
: state(state) | |
{ | |
} | |
node(configuration &&state) | |
: state(std::move(state)) | |
{ | |
} | |
node(const node &other) | |
: state(other.state), cost(other.cost) | |
{ | |
} | |
node(node &&other) | |
: state(std::move(other.state)), cost(std::move(other.cost)) | |
{ | |
} | |
node & operator=(const node &other) | |
{ | |
state = other.state; | |
cost = other.cost; | |
return *this; | |
} | |
node & operator=(node &&other) | |
{ | |
state = std::move(other.state); | |
cost = std::move(other.cost); | |
return *this; | |
} | |
}; | |
struct compare_node | |
{ | |
bool operator()(const node &n1, const node &n2) const | |
{ | |
return n1.cost > n2.cost; | |
} | |
}; | |
template <typename T> | |
void hash_combine(std::size_t &seed, const T &v) | |
{ | |
std::hash<T> hasher; | |
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); | |
} | |
template<typename It> | |
void hash_range(std::size_t &seed, It first, It last) | |
{ | |
for (; first != last; ++first) | |
hash_combine(seed, *first); | |
} | |
struct hash_state | |
{ | |
size_t operator()(const configuration &state) const | |
{ | |
size_t result = 0; | |
std::for_each(state.pegs.cbegin(), state.pegs.cend(), [&result](const std::vector<int> &peg) { hash_range(result, peg.cbegin(), peg.cend()); }); | |
return result; | |
} | |
}; | |
struct equal_to_state | |
{ | |
bool operator()(const configuration &c1, const configuration &c2) const | |
{ | |
return c1 == c2; | |
} | |
}; | |
size_t estimate_distance(const configuration ¤t_state, const configuration &final_state) | |
{ | |
size_t distance = 0; | |
for (size_t i = 0, k = current_state.pegs.size(); i < k; i++) | |
{ | |
const auto &p1 = current_state.pegs[i]; | |
const auto &p2 = final_state.pegs[i]; | |
size_t min, max; | |
std::tie(min, max) = std::minmax(p1.size(), p2.size()); | |
for (size_t j = 0; j < min; j++) | |
if (p1[j] != p2[j]) | |
distance++; | |
distance += max - min; | |
} | |
return distance / 2; | |
} | |
struct solver | |
{ | |
configuration initial_state; | |
configuration final_state; | |
std::unordered_set<configuration, hash_state, equal_to_state> closed; | |
std::vector<node> fringe; | |
size_t expanded, generated; | |
solver(configuration initial_state, configuration final_state) | |
: initial_state(std::move(initial_state)), final_state(std::move(final_state)), expanded(), generated() | |
{ | |
} | |
void expand(const configuration ¤t_configuration) | |
{ | |
for (size_t i = 0, k = current_configuration.pegs.size(); i < k; ++i) | |
if (!current_configuration.pegs[i].empty()) | |
{ | |
auto d = current_configuration.pegs[i].back(); | |
for (size_t j = 0; j < k; ++j) | |
if ((current_configuration.pegs[j].empty() || d <= current_configuration.pegs[j].back()) && i != j) | |
{ | |
node new_node(current_configuration); | |
new_node.state.pegs[i].pop_back(); | |
new_node.state.pegs[j].push_back(d); | |
generated++; | |
if (closed.find(new_node.state) == closed.cend()) | |
{ | |
new_node.cost = new_node.state.age + estimate_distance(new_node.state, final_state); | |
new_node.state.move = std::make_tuple(i, j); | |
new_node.state.age = current_configuration.age + 1; | |
fringe.push_back(std::move(new_node)); | |
std::push_heap(fringe.begin(), fringe.end(), compare_node()); | |
} | |
} | |
} | |
} | |
void print_path(configuration current) const | |
{ | |
std::vector<std::tuple<size_t, size_t>> path; | |
while (current.age > 0) | |
{ | |
auto i = std::get<0>(current.move); | |
auto j = std::get<1>(current.move); | |
path.push_back(std::move(current.move)); | |
current.pegs[i].push_back(current.pegs[j].back()); | |
current.pegs[j].pop_back(); | |
current = std::move(*closed.find(current)); | |
} | |
std::cout << path.size() << std::endl; | |
for (auto it = path.crbegin(), re = path.crend(); it != re; ++it) | |
std::cout << std::get<0>(*it) + 1 << ' ' << std::get<1>(*it) + 1 << std::endl; | |
} | |
void run() | |
{ | |
node initial_node(initial_state); | |
initial_node.cost = estimate_distance(initial_state, final_state); | |
fringe.push_back(std::move(initial_node)); | |
while (!fringe.empty()) | |
{ | |
std::pop_heap(fringe.begin(), fringe.end(), compare_node()); | |
auto current = std::move(fringe.back().state); | |
fringe.pop_back(); | |
if (current == final_state) | |
{ | |
//print_path(std::move(current)); | |
break; | |
} | |
expand(current); | |
expanded++; | |
closed.insert(std::move(current)); | |
} | |
} | |
}; | |
configuration read_configuration(std::istream &stream, int n, int k) | |
{ | |
configuration state; | |
state.age = 0; | |
for (int i = 0; i < k; i++) | |
state.pegs.emplace_back(); | |
for (int i = 0; i < n; i++) | |
{ | |
int peg_number; | |
stream >> peg_number; | |
auto &peg = state.pegs[peg_number - 1]; | |
peg.insert(peg.begin(), i + 1); | |
} | |
return state; | |
} | |
int main() | |
{ | |
{ | |
std::ifstream f("in.txt"); | |
int n, k; | |
f >> n >> k; | |
auto initial_state = read_configuration(f, n, k); | |
auto final_state = read_configuration(f, n, k); | |
auto start = std::chrono::high_resolution_clock::now(); | |
solver solver(std::move(initial_state), std::move(final_state)); | |
solver.run(); | |
auto stop = std::chrono::high_resolution_clock::now(); | |
std::cout << std::endl << solver.generated << " nodes generated, " << solver.expanded << " nodes expanded in " << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count() << " ms" << std::endl; | |
} | |
_CrtDumpMemoryLeaks(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _NDEBUG | |
#define _CRTDBG_MAP_ALLOC | |
#include <stdlib.h> | |
#include <crtdbg.h> | |
#endif | |
#include <algorithm> | |
#include <chrono> | |
#include <fstream> | |
#include <functional> | |
#include <iostream> | |
#include <tuple> | |
#include <unordered_set> | |
#include <vector> | |
struct configuration | |
{ | |
std::vector<std::vector<int>> pegs; | |
std::tuple<size_t, size_t> move; | |
size_t age; | |
configuration() | |
{ | |
} | |
configuration(const configuration &other) | |
: pegs(other.pegs), move(other.move), age(other.age) | |
{ | |
} | |
configuration(configuration &&other) | |
: pegs(std::move(other.pegs)), move(std::move(other.move)), age(std::move(other.age)) | |
{ | |
} | |
configuration & operator=(const configuration &other) | |
{ | |
pegs = other.pegs; | |
move = other.move; | |
age = other.age; | |
return *this; | |
} | |
configuration & operator=(configuration &&other) | |
{ | |
pegs = std::move(other.pegs); | |
move = std::move(other.move); | |
age = std::move(other.age); | |
return *this; | |
} | |
}; | |
bool operator==(const configuration &s1, const configuration &s2) | |
{ | |
return s1.pegs == s2.pegs; | |
} | |
bool operator!=(const configuration &s1, const configuration &s2) | |
{ | |
return s1.pegs != s2.pegs; | |
} | |
struct node | |
{ | |
configuration state; | |
size_t cost; | |
node(const configuration &state) | |
: state(state) | |
{ | |
} | |
node(configuration &&state) | |
: state(std::move(state)) | |
{ | |
} | |
node(const node &other) | |
: state(other.state), cost(other.cost) | |
{ | |
} | |
node(node &&other) | |
: state(std::move(other.state)), cost(std::move(other.cost)) | |
{ | |
} | |
node & operator=(const node &other) | |
{ | |
state = other.state; | |
cost = other.cost; | |
return *this; | |
} | |
node & operator=(node &&other) | |
{ | |
state = std::move(other.state); | |
cost = std::move(other.cost); | |
return *this; | |
} | |
}; | |
struct compare_node | |
{ | |
bool operator()(const node &n1, const node &n2) const | |
{ | |
return n1.cost > n2.cost; | |
} | |
}; | |
template <typename T> | |
void hash_combine(std::size_t &seed, const T &v) | |
{ | |
std::hash<T> hasher; | |
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); | |
} | |
template<typename It> | |
void hash_range(std::size_t &seed, It first, It last) | |
{ | |
for (; first != last; ++first) | |
hash_combine(seed, *first); | |
} | |
struct hash_state | |
{ | |
size_t operator()(const configuration &state) const | |
{ | |
size_t result = 0; | |
std::for_each(state.pegs.cbegin(), state.pegs.cend(), [&result](const std::vector<int> &peg) { hash_range(result, peg.cbegin(), peg.cend()); }); | |
return result; | |
} | |
}; | |
struct equal_to_state | |
{ | |
bool operator()(const configuration &c1, const configuration &c2) const | |
{ | |
return c1 == c2; | |
} | |
}; | |
size_t estimate_distance(const configuration ¤t_state, const configuration &final_state) | |
{ | |
size_t distance = 0; | |
for (size_t i = 0, k = current_state.pegs.size(); i < k; i++) | |
{ | |
const auto &p1 = current_state.pegs[i]; | |
const auto &p2 = final_state.pegs[i]; | |
size_t min, max; | |
std::tie(min, max) = std::minmax(p1.size(), p2.size()); | |
for (size_t j = 0; j < min; j++) | |
if (p1[j] != p2[j]) | |
distance++; | |
distance += max - min; | |
} | |
return distance / 2; | |
} | |
struct solver | |
{ | |
configuration initial_state; | |
configuration final_state; | |
std::unordered_set<configuration, hash_state, equal_to_state> closed; | |
std::vector<node> fringe; | |
size_t expanded, generated; | |
solver(configuration initial_state, configuration final_state) | |
: initial_state(std::move(initial_state)), final_state(std::move(final_state)), expanded(), generated() | |
{ | |
} | |
void expand(const configuration ¤t_configuration) | |
{ | |
for (size_t i = 0, k = current_configuration.pegs.size(); i < k; ++i) | |
if (!current_configuration.pegs[i].empty()) | |
{ | |
auto d = current_configuration.pegs[i].back(); | |
for (size_t j = 0; j < k; ++j) | |
if ((current_configuration.pegs[j].empty() || d <= current_configuration.pegs[j].back()) && i != j) | |
{ | |
node new_node(current_configuration); | |
new_node.state.pegs[i].pop_back(); | |
new_node.state.pegs[j].push_back(d); | |
generated++; | |
if (closed.find(new_node.state) == closed.cend()) | |
{ | |
new_node.cost = new_node.state.age + estimate_distance(new_node.state, final_state); | |
new_node.state.move = std::make_tuple(i, j); | |
new_node.state.age = current_configuration.age + 1; | |
fringe.push_back(std::move(new_node)); | |
std::push_heap(fringe.begin(), fringe.end(), compare_node()); | |
} | |
} | |
} | |
} | |
void print_path(configuration current) const | |
{ | |
std::vector<std::tuple<size_t, size_t>> path; | |
while (current.age > 0) | |
{ | |
auto i = std::get<0>(current.move); | |
auto j = std::get<1>(current.move); | |
path.push_back(std::move(current.move)); | |
current.pegs[i].push_back(current.pegs[j].back()); | |
current.pegs[j].pop_back(); | |
current = std::move(*closed.find(current)); | |
} | |
std::cout << path.size() << std::endl; | |
for (auto it = path.crbegin(), re = path.crend(); it != re; ++it) | |
std::cout << std::get<0>(*it) + 1 << ' ' << std::get<1>(*it) + 1 << std::endl; | |
} | |
void run() | |
{ | |
node initial_node(initial_state); | |
initial_node.cost = estimate_distance(initial_state, final_state); | |
fringe.push_back(std::move(initial_node)); | |
while (!fringe.empty()) | |
{ | |
std::pop_heap(fringe.begin(), fringe.end(), compare_node()); | |
auto current = std::move(fringe.back().state); | |
fringe.pop_back(); | |
if (current == final_state) | |
{ | |
//print_path(std::move(current)); | |
break; | |
} | |
expand(current); | |
expanded++; | |
closed.insert(std::move(current)); | |
} | |
} | |
}; | |
configuration read_configuration(std::istream &stream, int n, int k) | |
{ | |
configuration state; | |
state.age = 0; | |
for (int i = 0; i < k; i++) | |
state.pegs.emplace_back(); | |
for (int i = 0; i < n; i++) | |
{ | |
int peg_number; | |
stream >> peg_number; | |
auto &peg = state.pegs[peg_number - 1]; | |
peg.insert(peg.begin(), i + 1); | |
} | |
return state; | |
} | |
int main() | |
{ | |
{ | |
std::ifstream f("in.txt"); | |
int n, k; | |
f >> n >> k; | |
auto initial_state = read_configuration(f, n, k); | |
auto final_state = read_configuration(f, n, k); | |
auto start = std::chrono::high_resolution_clock::now(); | |
solver solver(std::move(initial_state), std::move(final_state)); | |
solver.run(); | |
auto stop = std::chrono::high_resolution_clock::now(); | |
std::cout << std::endl << solver.generated << " nodes generated, " << solver.expanded << " nodes expanded in " << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count() << " ms" << std::endl; | |
} | |
_CrtDumpMemoryLeaks(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment