2020-21
This commit is contained in:
170
dlx/src/dlx.cpp
Normal file
170
dlx/src/dlx.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
// See http://en.wikipedia.org/wiki/Dancing_Links.
|
||||
|
||||
#include "dlx.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
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<void(std::size_t, std::size_t, std::size_t)> try_cb,
|
||||
std::function<void()> undo_cb,
|
||||
std::function<void()> found_cb,
|
||||
std::function<void(std::size_t)> 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<std::size_t>::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);
|
||||
}
|
Reference in New Issue
Block a user