This commit is contained in:
Eric Mertens 2022-11-20 10:09:23 -08:00
parent eac45695b5
commit 50640e6adb
2 changed files with 122 additions and 102 deletions

View File

@ -7,7 +7,7 @@
namespace dlx { namespace dlx {
class Dlx { class Dlx final {
struct Cell { struct Cell {
Cell *U, *D, *L, *R; Cell *U, *D, *L, *R;
@ -17,42 +17,18 @@ class Dlx {
std::size_t s; std::size_t s;
}; };
auto LR_self() -> void { L = R = this; } auto LR_self() -> void;
auto UD_self() -> void { U = D = this; } auto UD_self() -> void;
auto LR_delete() -> void { L->R = R; R->L = L; } auto LR_delete() -> void;
auto UD_delete() -> void { U->D = D; D->U = U; } auto UD_delete() -> void;
auto UD_restore() -> void { U->D = D->U = this; } auto UD_restore() -> void;
auto LR_restore() -> void { L->R = R->L = this; } auto LR_restore() -> void;
auto LR_insert(Cell* k) -> void { L = k->L; R = k; k->L = k->L->R = this; } auto LR_insert(Cell* k) -> void;
auto UD_insert(Cell* k) -> void { U = k->U, D = k, k->U = k->U->D = this; } auto UD_insert(Cell* k) -> void;
auto CoverCol() -> void;
static Cell* ColNew() { auto UncoverCol() -> void;
auto c = new Cell; auto ColAdd(std::size_t row_id) -> Cell*;
c->UD_self(); static Cell* ColNew();
c->s = 0;
return c;
}
auto CoverCol() -> void {
LR_delete();
for (auto i = D; i != this; i = i->D) {
for (auto j = i->R; j != i; j = j->R) {
j->UD_delete();
j->c->s--;
}
}
}
auto UncoverCol() -> void {
for (auto i = U; i != this; i = i->U) {
for (auto j = i->L; j != i; j = j->L) {
j->c->s++;
j->UD_restore();
}
}
LR_restore();
}
}; };
std::vector<Cell*> ctab_; std::vector<Cell*> ctab_;
@ -68,6 +44,12 @@ public:
Dlx(); Dlx();
~Dlx(); ~Dlx();
Dlx(Dlx const&) = delete;
auto operator=(Dlx const&) -> Dlx& = delete;
Dlx(Dlx &&);
auto operator=(Dlx &&) -> Dlx&;
// Returns number of rows. // Returns number of rows.
auto Rows() const -> std::size_t; auto Rows() const -> std::size_t;
@ -82,14 +64,14 @@ public:
// but it still must respect the constraints it entails. // but it still must respect the constraints it entails.
auto MarkOptional(std::size_t col) -> void; auto MarkOptional(std::size_t col) -> void;
// Removes a row from consideration. Returns 0 on success, -1 otherwise. // Removes a row from consideration.
// Should only be called after all dlx_set() calls. // Should only be called after all dlx_set() calls.
auto RemoveRow(std::size_t row) -> int; auto RemoveRow(std::size_t row) -> void;
// Picks a row to be part of the solution. Returns 0 on success, -1 otherwise. // Picks a row to be part of the solution.
// Should only be called after all dlx_set() calls and dlx_remove_row() calls. // Should only be called after all Set() calls and RemoveRows() calls.
// TODO: Check the row can be legally chosen. // TODO: Check the row can be legally chosen.
auto PickRow(std::size_t row) -> int; auto PickRow(std::size_t row) -> void;
auto Solve( auto Solve(
std::function<void(std::size_t, std::size_t, std::size_t)> try_cb, std::function<void(std::size_t, std::size_t, std::size_t)> try_cb,

View File

@ -4,11 +4,56 @@
#include <limits> #include <limits>
using namespace dlx; namespace dlx {
auto Dlx::Cell::LR_self() -> void { L = R = this; }
auto Dlx::Cell::UD_self() -> void { U = D = this; }
auto Dlx::Cell::LR_delete() -> void { L->R = R; R->L = L; }
auto Dlx::Cell::UD_delete() -> void { U->D = D; D->U = U; }
auto Dlx::Cell::UD_restore() -> void { U->D = D->U = this; }
auto Dlx::Cell::LR_restore() -> void { L->R = R->L = this; }
auto Dlx::Cell::LR_insert(Cell* k) -> void { L = k->L; R = k; k->L = k->L->R = this; }
auto Dlx::Cell::UD_insert(Cell* k) -> void { U = k->U, D = k, k->U = k->U->D = this; }
auto Dlx::Cell::ColNew() -> Dlx::Cell*
{
auto c = new Cell;
c->UD_self();
c->s = 0;
return c;
}
auto Dlx::Cell::ColAdd(std::size_t row_id) -> Cell*
{
auto n = new Cell;
n->n = row_id;
n->c = this;
this->s++;
n->UD_insert(this);
return n;
}
auto Dlx::Cell::CoverCol() -> void {
LR_delete();
for (auto i = D; i != this; i = i->D) {
for (auto j = i->R; j != i; j = j->R) {
j->UD_delete();
j->c->s--;
}
}
}
auto Dlx::Cell::UncoverCol() -> void {
for (auto i = U; i != this; i = i->U) {
for (auto j = i->L; j != i; j = j->L) {
j->c->s++;
j->UD_restore();
}
}
LR_restore();
}
#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() { Dlx::Dlx() {
root_ = Cell::ColNew(); root_ = Cell::ColNew();
@ -32,27 +77,38 @@ Dlx::~Dlx() {
delete root_; delete root_;
} }
Dlx::Dlx(Dlx && dlx) : ctab_{}, rtab_{}, root_{}
{
std::swap(ctab_, dlx.ctab_);
std::swap(rtab_, dlx.rtab_);
std::swap(root_, dlx.root_);
}
auto Dlx::operator=(Dlx && dlx) -> Dlx& {
if (this != &dlx) {
this->~Dlx();
new (this) Dlx(std::move(dlx));
}
return *this;
}
auto Dlx::Rows() const -> std::size_t { return rtab_.size(); } auto Dlx::Rows() const -> std::size_t { return rtab_.size(); }
auto Dlx::Cols() const -> std::size_t { return ctab_.size(); } auto Dlx::Cols() const -> std::size_t { return ctab_.size(); }
auto Dlx::AddCol() -> void { auto Dlx::AllocCol(std::size_t n) -> void {
while(Cols() <= n) {
auto c = Cell::ColNew(); auto c = Cell::ColNew();
c->LR_insert(root_); c->LR_insert(root_);
c->n = Cols(); c->n = Cols();
ctab_.push_back(c); 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 { auto Dlx::AllocRow(std::size_t n) -> void {
while(Rows() <= n) AddRow(); while (Rows() <= n) {
rtab_.push_back(nullptr);
}
} }
auto Dlx::MarkOptional(std::size_t col) -> void { auto Dlx::MarkOptional(std::size_t col) -> void {
AllocCol(col); AllocCol(col);
@ -64,29 +120,13 @@ auto Dlx::MarkOptional(std::size_t col) -> void {
auto Dlx::Set(std::size_t row, std::size_t col) -> void { 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); AllocRow(row);
AllocCol(col); AllocCol(col);
auto c = ctab_[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]; auto & r = rtab_[row];
if (!r) { if (!r) {
r = new1(); r = c->ColAdd(row);
r->LR_self(); r->LR_self();
return; return;
} }
@ -99,24 +139,20 @@ auto Dlx::Set(std::size_t row, std::size_t col) -> void {
} }
// Otherwise insert at end of LR list. // Otherwise insert at end of LR list.
new1()->LR_insert(r); c->ColAdd(row)->LR_insert(r);
} }
auto Dlx::PickRow(std::size_t i) -> int { auto Dlx::PickRow(std::size_t i) -> void {
auto r = rtab_.at(i); if (auto r = rtab_.at(i)) {
if (!r) return 0; // Empty row.
r->c->CoverCol(); r->c->CoverCol();
for (auto j = r->R; j != r; j = j->R) { for (auto j = r->R; j != r; j = j->R) {
j->c->CoverCol(); j->c->CoverCol();
} }
}
return 0;
} }
auto Dlx::RemoveRow(std::size_t i) -> void {
auto Dlx::RemoveRow(std::size_t i) -> int { if (auto & r = rtab_.at(i)) {
auto & r = rtab_.at(i);
if (!r) return 0; // Empty row.
r->UD_delete(); r->UD_delete();
r->c->s--; r->c->s--;
for (auto j = r->R; j != r; j = j->R) { for (auto j = r->R; j != r; j = j->R) {
@ -124,7 +160,7 @@ auto Dlx::RemoveRow(std::size_t i) -> int {
j->c->s--; j->c->s--;
} }
r = nullptr; r = nullptr;
return 0; }
} }
auto Dlx::Solve( auto Dlx::Solve(
@ -140,17 +176,17 @@ auto Dlx::Solve(
return; return;
} }
auto s = std::numeric_limits<std::size_t>::max(); // S-heuristic: choose first most-constrained column. auto s = std::numeric_limits<std::size_t>::max();
for (auto i = root_->R; i != root_; i = i->R) { for (auto i = root_->R; i != root_; i = i->R) {
if (i->s < s) { if (i->s < s) {
s = (c = i)->s; s = (c = i)->s;
}
}
if (!s) { if (s == 0) {
stuck_cb(c->n); stuck_cb(c->n);
return; return;
} }
}
}
c->CoverCol(); c->CoverCol();
for (auto r = c->D; r != c; r = r->D) { for (auto r = c->D; r != c; r = r->D) {
@ -168,3 +204,5 @@ auto Dlx::Solve(
}; };
recurse(recurse); recurse(recurse);
} }
} // namespace