#include #include #include #include #include #include #include #include #include #include struct Matrix { int m, n; int *data; Matrix(int m, int n) : m{m}, n{n}, data{new int[m * n]} { } Matrix(const Matrix&) = delete; ~Matrix() { delete[] data; } auto size() const { return m * n; } bool in_bounds(int i) const { return i >= 0 && i < m * n; } bool in_bounds(int i, int j) const { return i >= 0 && j >= 0 && i < m && j < n; } int& operator[] (int i) { return data[i]; } int operator[] (int i) const { return data[i]; } int& at(int i, int j) { return data[i * n + j]; } int at(int i, int j) const { return data[i * n + j]; } }; struct Board { int colors; Matrix pixels; Board(int width, int height, int colors) : colors{colors}, pixels{height, width} { } void randomize() { std::random_device rd{}; std::mt19937 gen{rd()}; std::uniform_int_distribution<> dis{0, colors - 1}; for (int i = 0; i < pixels.m; ++i) { for (int j = 0; j < pixels.n; ++j) { pixels.at(i, j) = dis(gen); } } } void inflate(std::string filename) { std::ifstream ifs{filename}; std::string line; for (int i = 0; i < pixels.m; ++i) { std::getline(ifs, line); for (int j = 0; j < pixels.n; ++j) { pixels.at(i, j) = static_cast(line[j] - '0'); } } } std::string stringify() const { std::ostringstream oss; for (int i = 0; i < pixels.m; ++i) { for (int j = 0; j < pixels.n; ++j) { oss << pixels.at(i, j); } oss << '\n'; } auto str = oss.str(); str.erase(str.end() - 1); return str; } template void foreach_neighbor_of(int i, Func f) const { int idiv = i / pixels.n; int imod = i % pixels.n; // up if (idiv != 0) { f(i - pixels.n); } // left if (imod != 0) { f(i - 1); } // right if (imod != pixels.n - 1) { f(i + 1); } // down if (idiv != pixels.m - 1) { f(i + pixels.n); } } }; std::ostream& operator<< (std::ostream& os, const Board& board) { return os << board.stringify(); } struct Island { int color; std::vector elements; Island(int color) : color{color} { } Island(Island&& rhs) : color{rhs.color}, elements{std::move(rhs.elements)} { } }; template void uniquify(Container& cont) { std::sort(std::begin(cont), std::end(cont)); auto last_unique = std::unique(std::begin(cont), std::end(cont)); cont.erase(last_unique, std::end(cont)); } auto find_islands(const Board& board) { std::vector visited(board.pixels.size(), false); std::vector islands{}; std::vector visit_queue{}; for (int i = 0; i < board.pixels.size(); ++i) { if (visited[i]) continue; visit_queue.clear(); visit_queue.push_back(i); auto color = board.pixels[i]; Island island { color }; int visit_head = 0; while (visit_head < visit_queue.size()) { auto j = visit_queue[visit_head]; visit_head += 1; if (visited[j]) continue; if (color == board.pixels[j]) { visited[j] = true; island.elements.push_back(j); board.foreach_neighbor_of(j, [&] (int x) { visit_queue.push_back(x); }); } } islands.emplace_back(std::move(island)); } return islands; } auto islands_adjacency(const Board& board, const std::vector& islands) { std::vector> adjacency{}; std::vector elem2island{}; adjacency.reserve(islands.size()); elem2island.reserve(board.pixels.size()); for (int i = 0; i < islands.size(); ++i) { for (int elem : islands[i].elements) { elem2island[elem] = i; } } for (auto&& island : islands) { std::vector friends{}; for (int elem : island.elements) { board.foreach_neighbor_of(elem, [&] (int neighbor) { if (island.color == board.pixels[neighbor]) return; friends.push_back(elem2island[neighbor]); }); } uniquify(friends); adjacency.emplace_back(std::move(friends)); } return adjacency; } struct NatSet { int count; boost::dynamic_bitset<> table; NatSet(const NatSet& rhs) : count{rhs.count}, table{rhs.table} { } NatSet& operator= (const NatSet& rhs) { count = rhs.count; table = rhs.table; return *this; } NatSet(NatSet&& rhs) : count{rhs.count}, table{std::move(rhs.table)} { } NatSet& operator= (NatSet&& rhs) { count = rhs.count; table = std::move(rhs.table); return *this; } NatSet(int max, std::initializer_list elems) : count{0}, table(max, false) { insert(elems); } void insert(int e) { if (!table[e]) { count += 1; } table[e] = true; } template void insert(const Container& cont) { for (auto&& x : cont) { table[x] = true; } recount(); } bool contains(int e) const { return table[e]; } bool is_subset_of(const NatSet& rhs) const { return table.is_subset_of(rhs.table); } private: void recount() { count = table.count(); } }; struct SolveState { NatSet domain; std::vector border; std::vector steps; SolveState(const SolveState& rhs) : SolveState{rhs.domain, rhs.border, rhs.steps} { } SolveState& operator= (const SolveState& rhs) { domain = rhs.domain; border = rhs.border; steps = rhs.steps; return *this; } SolveState(SolveState&& rhs) : domain{std::move(rhs.domain)} , border{std::move(rhs.border)} , steps {std::move(rhs.steps)} { } SolveState& operator= (SolveState&& rhs) { domain = std::move(rhs.domain); border = std::move(rhs.border); steps = std::move(rhs.steps); return *this; } SolveState(NatSet ns) : SolveState{std::move(ns), {}, {}} { } SolveState(NatSet ns, std::vector border) : SolveState{std::move(ns), std::move(border), {}} { } SolveState(NatSet ns, std::vector border, std::vector steps) : domain{std::move(ns)}, border{std::move(border)}, steps{std::move(steps)} { } }; auto solve(const Board& board) { auto islands = find_islands(board); auto adjacency = islands_adjacency(board, islands); std::vector level{}; auto init = NatSet{board.pixels.size(), {0}}; level.emplace_back(init, adjacency[0]); std::vector next{}; while (true) { next.clear(); for (auto&& state : level) { for (int color = 0; color < board.colors; ++color) { NatSet domain{state.domain}; std::vector border{}; for (int friend_ : state.border) { if (islands[friend_].color == color) { domain.insert(friend_); for (int poop : adjacency[friend_]) { if (!domain.contains(poop)) { border.push_back(poop); } } } else { border.push_back(friend_); } } if (state.domain.count == domain.count) { continue; } std::vector steps{state.steps}; steps.push_back(color); if (domain.count == islands.size()) { return steps; } uniquify(border); next.emplace_back(std::move(domain), std::move(border), std::move(steps)); } } std::sort(std::begin(next), std::end(next), [] (const SolveState& a, const SolveState& b) { return a.domain.count < b.domain.count; }); level.clear(); for (int i = 0; i < next.size(); ++i) { bool skip = false; for (int j = i + 1; j < next.size(); ++j) { if (next[i].domain.is_subset_of(next[j].domain)) { skip = true; break; } } if (!skip) { level.emplace_back(std::move(next[i])); } } } } template void print_container(std::ostream& os, const Container& cont) { os << '['; auto it = std::begin(cont); auto ot = std::end(cont); if (it != ot) { os << *it; } ++it; for ( ; it != ot; ++it) { os << ", " << *it; } os << "]\n"; } int main() { Board board{16, 9, 6}; //board.randomize(); board.inflate("board.txt"); std::cout << board << std::endl; /* auto islands = find_islands(board); for (auto&& island : islands) { for (auto&& x : island.elements) { std::cout << x << ' '; } std::cout << '\n'; } auto adjacency = islands_adjacency(board, islands); for (auto&& row : adjacency) { for (auto&& x : row) { std::cout << x << ' '; } std::cout << '\n'; } */ auto answer = solve(board); print_container(std::cout, answer); return 0; }