more dlx
This commit is contained in:
parent
eac45695b5
commit
50640e6adb
|
@ -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,
|
||||||
|
|
158
dlx/src/dlx.cpp
158
dlx/src/dlx.cpp
|
@ -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,28 +77,39 @@ 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 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 {
|
auto Dlx::AllocCol(std::size_t n) -> void {
|
||||||
while(Cols() <= n) AddCol();
|
while(Cols() <= n) {
|
||||||
|
auto c = Cell::ColNew();
|
||||||
|
c->LR_insert(root_);
|
||||||
|
c->n = Cols();
|
||||||
|
ctab_.push_back(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
auto c = ctab_[col];
|
auto c = ctab_[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,32 +139,28 @@ 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);
|
r->UD_delete();
|
||||||
if (!r) return 0; // Empty row.
|
r->c->s--;
|
||||||
r->UD_delete();
|
for (auto j = r->R; j != r; j = j->R) {
|
||||||
r->c->s--;
|
j->UD_delete();
|
||||||
for (auto j = r->R; j != r; j = j->R) {
|
j->c->s--;
|
||||||
j->UD_delete();
|
}
|
||||||
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 == 0) {
|
||||||
|
stuck_cb(c->n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s) {
|
|
||||||
stuck_cb(c->n);
|
|
||||||
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
|
Loading…
Reference in New Issue
Block a user