package backtracking;
import java.util.LinkedList;
class DLXNode {
DLXNode left, right, up, down, col;
String name; // seulement pour les entêtes, null sinon
int size;
// crée une nouvelle colonne
public DLXNode(String name) {
this.left = this;
this.right = this;
this.up = this;
this.down = this;
this.col = null;
this.name = name;
this.size = 0;
}
// crée un noeud, en bas de la colonne c
public DLXNode(DLXNode c) {
this((String)null); // this(null) serait ambigu
this.col = c;
c.size++;
this.down = c.down;
c.down = this;
this.up = c;
this.down.up = this;
}
boolean isColumn() { return this.name != null; }
boolean isNode() { return this.name == null; }
// lie les noeuds de l horizontalement
static void makeLRlist(LinkedList<DLXNode> l) {
DLXNode first = l.poll();
DLXNode prev = first;
for (DLXNode n: l) {
prev.right = n;
n.left = prev;
prev = n;
}
prev.right = first;
first.left = prev;
}
static void printLR(DLXNode n) {
System.err.print(n.name);
for (DLXNode x = n.right; x != n; x = x.right)
System.err.print(" " + x.name);
System.err.println();
}
static void printSolution(LinkedList<DLXNode> sol) {
if (sol == null) return;
for (DLXNode row: sol) {
System.out.print(row.col.name);
for (DLXNode x = row.right; x != row; x = x.right)
System.out.print(" " + x.col.name);
System.out.println();
}
}
// supprime et réinsère horizontalement
void removeLR() {
this.left.right = this.right;
this.right.left = this.left;
}
void restoreLR() {
this.right.left = this;
this.left.right = this;
}
// supprime et réinsère verticalement
void removeUD() {
this.up.down = this.down;
this.down.up = this.up;
}
void restoreUD() {
this.down.up = this;
this.up.down = this;
}
}
public class DancingLinks {
DLXNode header;
DancingLinks(LinkedList<DLXNode> columns) {
this.header = new DLXNode("header");
columns.add(this.header);
DLXNode.makeLRlist(columns);
DLXNode.printLR(header);
}
void addRow(LinkedList<DLXNode> row) {
LinkedList<DLXNode> nodes = new LinkedList<>();
for (DLXNode c: row) {
assert c.isColumn();
nodes.add(new DLXNode(c));
}
DLXNode.makeLRlist(nodes);
}
void cover(DLXNode c) {
assert c.isColumn();
c.removeLR();
for (DLXNode x = c.down; x != c; x = x.down)
for (DLXNode y = x.right; y != x; y = y.right) {
y.removeUD();
y.col.size--;
}
}
void uncover(DLXNode c) {
assert c.isColumn();
for (DLXNode x = c.up; x != c; x = x.up)
for (DLXNode y = x.left; y != x; y = y.left) {
y.col.size++;
y.restoreUD();
}
c.restoreLR();
}
// la colonne minimisant size
DLXNode minColumn() {
DLXNode best = header.right;
for (DLXNode c = header.right.right; c != header; c = c.right)
if (c.size < best.size)
best = c;
//System.err.println("best = " + best.name);
return best;
}
// trouver une solution
LinkedList<DLXNode> sol;
boolean solveRec() {
if (header.right == header) return true;
DLXNode best = minColumn();
cover(best);
for (DLXNode row = best.down; row != best; row = row.down) {
sol.addLast(row);
for (DLXNode x = row.right; x != row; x = x.right)
cover(x.col);
if (solveRec()) return true;
for (DLXNode x = row.left; x != row; x = x.left)
uncover(x.col);
sol.removeLast();
}
uncover(best);
return false;
}
LinkedList<DLXNode> solve() {
sol = new LinkedList<>();
return solveRec() ? sol : null;
}
// compter les solutions
int count = 0;
void countRec() {
if (header.right == header) {
count++;
return;
}
DLXNode best = minColumn();
cover(best);
for (DLXNode row = best.down; row != best; row = row.down) {
for (DLXNode x = row.right; x != row; x = x.right)
cover(x.col);
countRec();
for (DLXNode x = row.left; x != row; x = x.left)
uncover(x.col);
}
uncover(best);
}
int count() {
count = 0;
countRec();
return count;
}
}