c++ 舞蹈鏈算法求解數獨問題 控制檯版本 帶註釋

#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#define DELPOINTER(p) if(nullptr!=(p)){delete[] (p),(p)=nullptr;}
#define DO(sth) for (DLXNode * pCol = pRow->right; pCol != pRow; pCol = pCol->right) {\
	(sth)(pCol->elem.c);\
}

using namespace std;

int ans[1000];

unordered_map<int, int> m;

struct ElemType {
	int r, c;
	ElemType(const int & _r = int(), const int & _c = int()) :r(_r), c(_c) {}
};

struct DLXNode {
	ElemType elem;
	DLXNode *up, *left, *down, *right;
	DLXNode(const ElemType & _elem = ElemType(),
		DLXNode * _up = nullptr, DLXNode * _left = nullptr, DLXNode * _down = nullptr, DLXNode * _right = nullptr)
		:elem(_elem), up(_up), left(_left), down(_down), right(_right) {}
};

class DLX {
public:
	DLX(const int & _row, const int & _col);
	DLX(const vector<vector<int> > & mtx);
	~DLX(void);

	inline void addNode(const int & x, const int & y);
	/* 雙向鏈表的斷開與連接操作 */
	/* U  L  D  R */
	/* 上 左 下 右 */
	inline void deleteUD(DLXNode * p);
	inline void deleteLR(DLXNode * p);
	inline void resumeUD(DLXNode * p);
	inline void resumeLR(DLXNode * p);

	void cover(const int & col);
	void uncover(const int & col);

	bool dance(const int & step);

	inline DLXNode* getNode(const int & index);
private:
	void init(const int & row, const int & col);
	inline bool empty() { return m_pHead->left == m_pHead; }
	inline int getMinColPos();	// 獲取當前所有列中元素個數最小的一列

private:
	int m_nIndex;	// 有多少個節點
	int m_nRowCount;	// 行數
	int m_nColCount;	// 列數
	int * m_pSz;	// 每一列有多少個節點
	DLXNode *m_pHead;	// 頭結點(入口)
	DLXNode *m_pRows, *m_pCols;	// 行指針與列指針
	DLXNode *m_pNodes;	// 所有的節點
};

DLX::DLX(const int & _row, const int & _col) {
	init(_row, _col);
}

DLX::DLX(const vector<vector<int> > & mtx) {
	/* 初始化 */
	init(mtx.size(), mtx[0].size());
	for (int i = m_nRowCount - 1; i >= 0; --i) {
		for (int j = 0; j < m_nColCount; ++j) {
			if (mtx[i][j]) {
				addNode(i, j);
			}
		}
	}
}

DLX::~DLX(void) {
	DELPOINTER(m_pSz);
	DELPOINTER(m_pHead);
	DELPOINTER(m_pRows);
	DELPOINTER(m_pCols);
	DELPOINTER(m_pNodes);
}

inline void DLX::addNode(const int & x, const int & y) {
	DLXNode * p = getNode(m_nIndex++);
	p->elem = ElemType(x, y);
	/*					前插					*/
	/* 向上的指針指向當前列的頭指針				*/
	/* 向下的指針指向當前列的頭指針的向下的指針 */
	/* 也就是最後一次插入當前列的那個結點(Node')*/
	/* 頭指針的向下指針指向當前節點				*/
	/* Node' 的向上指針指向當前節點				*/
	/* 此時當前節點位於頭結點和 Node' 之間		*/
	/* 頭節點 <--> p <--> Node'					*/
	p->up = &m_pCols[y];
	p->down = m_pCols[y].down;
	p->up->down = p->down->up = p;
	// 後插(原理同上)
	p->right = &m_pRows[x];
	p->left = m_pRows[x].left;
	p->left->right = p->right->left = p;
	++m_pSz[y];
}

inline DLXNode* DLX::getNode(const int & index) {
	return &m_pNodes[index];
}

inline void DLX::deleteUD(DLXNode * p) {
	p->up->down = p->down;
	p->down->up = p->up;
}

inline void DLX::deleteLR(DLXNode * p) {
	p->left->right = p->right;
	p->right->left = p->left;
}

inline void DLX::resumeUD(DLXNode * p) {
	p->up->down = p->down->up = p;
}

inline void DLX::resumeLR(DLXNode * p) {
	p->left->right = p->right->left = p;
}

void DLX::cover(const int & col) {
	if (col == m_nColCount) {
		return;
	}
	// 先刪除當前列的頭結點
	deleteLR(&m_pCols[col]);
	// 找到當前列的所有Node
	for (DLXNode * pCol = m_pCols[col].down; pCol != &m_pCols[col]; pCol = pCol->down) {
		if (pCol->elem.c == m_nColCount) {
			continue;
		}
		// 找到這一行與當前Node相連的Node,然後刪除這一行
		for (DLXNode * pRow = pCol->left; pRow != pCol; pRow = pRow->left) {
			if (pRow->elem.c == m_nColCount) {
				continue;
			}
			--m_pSz[pRow->elem.c];
			deleteUD(pRow);
		}
		deleteLR(pCol);
	}
}

void DLX::uncover(const int & col) {
	// 和cover是鏡像操作
	if (col == m_nColCount) {
		return;
	}
	for (DLXNode * pCol = m_pCols[col].down; pCol != &m_pCols[col]; pCol = pCol->down) {
		if (pCol->elem.c == m_nColCount) {
			continue;
		}
		resumeLR(pCol);
		for (DLXNode * pRow = pCol->left; pRow != pCol; pRow = pRow->left) {
			if (pRow->elem.c == m_nColCount) {
				continue;
			}
			++m_pSz[pRow->elem.c];
			resumeUD(pRow);
		}
	}
	resumeLR(&m_pCols[col]);
}

bool DLX::dance(const int & step) {
	if (empty()) {
		sort(ans, ans + step);
		// 輸出結果
		for (int i = 0; i < step; ++i) {
			if (0 == i % 9) {
				cout << endl;
			}
			cout << m[ans[i] + 1] << ' ';
		}
		return true;
	}
	int col = getMinColPos();
	/* 刪掉這一列 */
	cover(col);
	for (DLXNode * pRow = m_pCols[col].down; pRow != &m_pCols[col]; pRow = pRow->down) {
		/* 斷開循環鏈表 */
		pRow->left->right = pRow; 
		/* 刪除這一列對應的(所有行的節點(所在的列)) */
		DO(cover);
		ans[step] = pRow->elem.r;
		if (dance(step + 1)) {
			return true;
		}
		/* 恢復這一列對應的(所有行的節點(所在的列)) */
		DO(uncover);
		/* 連接循環鏈表 */
		pRow->left->right = pRow->right;
	}
	/* 恢復這一列 */
	uncover(col);
	return false;
}

void DLX::init(const int & row, const int & col) {
	m_nRowCount = row;
	m_nColCount = col;
	m_nIndex = 0;
	if (row <= 0 || col <= 0) {
		m_pSz = nullptr;
		m_pHead = m_pRows = m_pCols = m_pNodes = nullptr;
	}
	else {
		m_pSz = new int[col];
		m_pHead = new DLXNode;
		m_pRows = new DLXNode[row];
		m_pCols = new DLXNode[col];
		m_pNodes = new DLXNode[row * col];
		m_pHead->elem = ElemType(row, col);
		/* head的所有指針都指向自己 */
		m_pHead->up = m_pHead->left = m_pHead->down = m_pHead->right = m_pHead;
		/* 列指針初始化時上下指針指向自己 */
		for (int i = 0; i < col; ++i) {
			m_pCols[i].elem = ElemType(0, i);
			m_pCols[i].left = m_pHead;
			m_pCols[i].right = m_pHead->right;
			m_pCols[i].left->right = m_pCols[i].right->left = m_pCols[i].up = m_pCols[i].down = &m_pCols[i];
			m_pSz[i] = 0;
		}
		/* 行指針初始化時左右指針指向自己,並且因爲是前插,所有要倒序遍歷 */
		for (int i = row - 1; i >= 0; --i) {
			m_pRows[i].elem = ElemType(i, col);
			m_pRows[i].up = m_pHead;
			m_pRows[i].down = m_pHead->down;
			m_pRows[i].up->down = m_pRows[i].down->up = m_pRows[i].left = m_pRows[i].right = &m_pRows[i];
		}
	}
}

inline int DLX::getMinColPos() {
	int min = 0x3f3f3f3f;
	int col = -1;
	for (DLXNode * p = m_pHead->left; p != m_pHead; p = p->left) {
		if (m_pSz[p->elem.c] < min) {
			min = m_pSz[p->elem.c];
			col = p->elem.c;
		}
	}
	return col;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int N, M;
	cin >> N >> M;
	int cnt = 0;
	vector<vector<int> > mtx(N, vector<int>(M, 0));
	for (int i = 0; i < N; ++i) {
		for (int j = 0; j < M; ++j) {
			cin >> mtx[i][j];
			if (mtx[i][j]) {
				++cnt;
			}
		}
	}
	int row = cnt + (81 - cnt) * 9;
	int col = 324;
	cnt = 0;
	DLX dlx(row, col);
	for (int i = 0; i < N; ++i) {
		for (int j = 0; j < M; ++j) {
			if (mtx[i][j]) {
				dlx.addNode((cnt), (i) * 9 + (j)); 
				dlx.addNode((cnt), (i) * 9 + 81 + mtx[i][j] - 1);
				dlx.addNode((cnt), (j) * 9 + 162 + mtx[i][j] - 1);
				dlx.addNode((cnt), (i / 3 * 3 + j / 3) * 9 + 243 + mtx[i][j] - 1);
				m[++cnt] = mtx[i][j];
			}
			else {
				for (int k = 1; k <= 9; ++k) {
					dlx.addNode((cnt), (i) * 9 + (j));
					dlx.addNode((cnt), (i) * 9 + 81 + k - 1);
					dlx.addNode((cnt), (j) * 9 + 162 + k - 1);
					dlx.addNode((cnt), (i / 3 * 3 + j / 3) * 9 + 243 + k - 1);
					m[++cnt] = k;
				}
			}
		}
	}
	if (!dlx.dance(0)) {
		cout << "數獨無解";
	}
	return 0;
}
/*
9 9
7 6 0 5 9 3 0 0 0
9 0 1 0 0 0 5 0 0
0 3 0 4 0 0 0 9 0
1 0 8 0 2 0 0 0 4
4 0 0 3 0 9 0 0 1
2 0 0 0 1 0 6 0 9
0 8 0 0 0 6 0 2 0
0 0 4 0 0 0 8 0 7
0 0 0 7 8 5 0 1 0
*/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章