// See http://en.wikipedia.org/wiki/Dancing_Links. #include "dlx.hpp" #include using namespace dlx; #define F(i,n) for(int i = 0; i < n; i++) #define C(i,n,dir) for(cell_ptr i = (n)->dir; i != n; i = i->dir) Dlx::Dlx() { root_ = Cell::ColNew(); root_->LR_self(); } Dlx::~Dlx() { for (auto const row : rtab_) { if (row) { Cell* next; for (auto cursor = row->R; cursor != row; cursor = next) { next = cursor->R; delete cursor; } delete row; } } for (auto const col : ctab_) { if (col) { delete col; } } delete root_; } auto Dlx::Rows() const -> std::size_t { return rtab_.size(); } auto Dlx::Cols() const -> std::size_t { return ctab_.size(); } auto Dlx::AddCol() -> void { auto c = Cell::ColNew(); c->LR_insert(root_); c->n = Cols(); ctab_.push_back(c); } auto Dlx::AddRow() -> void { rtab_.push_back(nullptr); } auto Dlx::AllocCol(std::size_t n) -> void { while(Cols() <= n) AddCol(); } auto Dlx::AllocRow(std::size_t n) -> void { while(Rows() <= n) AddRow(); } auto Dlx::MarkOptional(std::size_t col) -> void { AllocCol(col); auto c = ctab_[col]; // Prevent undeletion by self-linking. c->LR_delete(); c->LR_self(); } auto Dlx::Set(std::size_t row, std::size_t col) -> void { // We don't bother sorting. DLX works fine with jumbled rows and columns. // We just have to watch out for duplicates. (Actually, I think the DLX code // works even with duplicates, though it would be inefficient.) // // For a given column, the UD list is ordered in the order that dlx_set() // is called, not by row number. Similarly for a given row and its LR list. AllocRow(row); AllocCol(col); auto c = ctab_[col]; auto const new1 = [&]() -> Cell* { auto n = new Cell; n->n = row; n->c = c; c->s++; n->UD_insert(c); return n; }; auto & r = rtab_[row]; if (!r) { r = new1(); r->LR_self(); return; } // Ignore duplicates. if (r->c->n == col) return; for (auto cursor = r->R; cursor != r; cursor = cursor->R) { if (cursor->c->n == col) return; } // Otherwise insert at end of LR list. new1()->LR_insert(r); } auto Dlx::PickRow(std::size_t i) -> int { auto r = rtab_.at(i); if (!r) return 0; // Empty row. r->c->CoverCol(); for (auto j = r->R; j != r; j = j->R) { j->c->CoverCol(); } return 0; } auto Dlx::RemoveRow(std::size_t i) -> int { auto & r = rtab_.at(i); if (!r) return 0; // Empty row. r->UD_delete(); r->c->s--; for (auto j = r->R; j != r; j = j->R) { j->UD_delete(); j->c->s--; } r = nullptr; return 0; } auto Dlx::Solve( std::function try_cb, std::function undo_cb, std::function found_cb, std::function stuck_cb) -> void { auto const recurse = [&](auto const& self) -> void { auto c = root_->R; if (c == root_) { found_cb(); return; } auto s = std::numeric_limits::max(); // S-heuristic: choose first most-constrained column. for (auto i = root_->R; i != root_; i = i->R) { if (i->s < s) { s = (c = i)->s; } } if (!s) { stuck_cb(c->n); return; } c->CoverCol(); for (auto r = c->D; r != c; r = r->D) { try_cb(c->n, s, r->n); for (auto j = r->R; j != r; j = j->R) { j->c->CoverCol(); } self(self); undo_cb(); for (auto j = r->L; j != r; j=j->L) { j->c->UncoverCol(); } } c->UncoverCol(); }; recurse(recurse); }