精确覆盖问题 DLX算法 Hihocoder1317,1321

两道入门题
HihoCoder1317
精确覆盖问题:在一个0-1矩阵中,选定部分行,使得每一列都有且只有一个1。求解一种选法。
当然使用搜索来解决,先选择一行,然后将不能选的行给删掉,然后再继续向下,完成后恢复现场。(X算法)
但是这样的方法,在对于矩阵的删除行列和恢复操作太复杂,所以使用一种数据结构--舞蹈链(Dance Link),也就是一个循环十字链表,可以快速的删掉和恢复某行某列。
结合了舞蹈链的搜索就称作DLX算法。
题目链接中有了很不错的分析。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn = 200;
const int maxr = 200;
const int maxnode = 1e6;
struct DLX
{
	int U[maxnode], D[maxnode], L[maxnode], R[maxnode];
	int row[maxnode], col[maxnode];
	int S[maxn], H[maxr], ans[maxr], ansd, sz;
	int n,m;

	void init(int n, int m){
		this->n = n; this->m = m;
		for(int i = 0; i <= m; i++){
			L[i] = i-1; R[i] = i+1; U[i] = D[i] = i;
		}
		L[0] = m; R[m] = 0; sz = m+1;
		memset(S, 0, sizeof(S));
		memset(H, -1, sizeof(H));
	}

	void addNode(int r, int c){
		row[sz] = r; col[sz] = c;
		U[sz] = U[c];  D[sz] = c;
		D[U[sz]] = sz; U[D[sz]] = sz;
		if(H[r] == -1){
			L[sz] = sz; R[sz] = sz;
			H[r] = sz;
		}else{
			R[sz] = H[r]; L[sz] = L[H[r]];
			L[R[sz]] = sz; R[L[sz]] = sz;
		}
		S[col[sz]]++; sz++;
	}
	#define FOR(i, A, s) for(int i = A[s]; i != s; i = A[i])
	void remove(int c){
		R[L[c]] = R[c]; L[R[c]] = L[c];
		FOR(i, D, c) FOR(j, R, i){
			U[D[j]] = U[j]; D[U[j]] = D[j]; S[col[j]]--;
		}
	}
	void restore(int c){
		FOR(i, U, c) FOR(j, L, i){
			U[D[j]] = j; D[U[j]] = j; S[col[j]]++;
		}
		R[L[c]] = c; L[R[c]] = c;
	}
	bool dfs(int d){
		if(R[0] == 0){
			ansd = d;
			return true;
		}
		int c = R[0];
		FOR(i, R, 0){
			if(S[i] < S[c]) c = i;
		}
		remove(c);
		FOR(i, D, c){
			ans[d] = row[i];
			FOR(j, R, i) remove(col[j]);
			if(dfs(d+1)) return true;
			FOR(j, L, i) restore(col[j]);
		}
		restore(c);
		return false;
	}
}dlx;
int t,n,m;
int main()
{
	cin >> t;
	while(t--){
		cin >> n >> m;
		dlx.init(n,m);
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= m; j++){
				int tmp;
				cin >> tmp;
				if(tmp) dlx.addNode(i,j);
			}
		}
		if(dlx.dfs(0)){
			cout << "Yes
";
		}else{
			cout << "No
";
		}
	}
	return 0;
}

HihoCoder1321
解数独问题:
可以转变成精确覆盖问题来解决。
每一行代表这一项决策,共有9*9*9行,i*9*9+j*9+k表示第i行j列的格子里面填k这项决策
每一列表示一个任务,共有4*9*9
有四类任务:

  • SLOT(i,j) 第i行j列要放当前的决策值
  • ROW(i,k) 第i行要有k值
  • COL(j,k) 第j列要有k值
  • SUB(s,k) 第s个九宫要有k值
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn = 1020;
const int maxr = 1020;
const int maxnode = 2e6;
struct DLX
{
	int U[maxnode], D[maxnode], L[maxnode], R[maxnode];
	int row[maxnode], col[maxnode];
	int S[maxn], H[maxr], ans[maxr], ansd, sz;
	int n,m;

	void init(int n, int m){
		this->n = n; this->m = m;
		for(int i = 0; i <= m; i++){
			L[i] = i-1; R[i] = i+1; U[i] = D[i] = i;
		}
		L[0] = m; R[m] = 0; sz = m+1;
		memset(S, 0, sizeof(S));
		memset(H, -1, sizeof(H));
	}

	void addNode(int r, int c){
		row[sz] = r; col[sz] = c;
		U[sz] = U[c];  D[sz] = c;
		D[U[sz]] = sz; U[D[sz]] = sz;
		if(H[r] == -1){
			L[sz] = sz; R[sz] = sz;
			H[r] = sz;
		}else{
			R[sz] = H[r]; L[sz] = L[H[r]];
			L[R[sz]] = sz; R[L[sz]] = sz;
		}
		S[col[sz]]++; sz++;
	}
	#define FOR(i, A, s) for(int i = A[s]; i != s; i = A[i])
	void remove(int c){
		R[L[c]] = R[c]; L[R[c]] = L[c];
		FOR(i, D, c) FOR(j, R, i){
			U[D[j]] = U[j]; D[U[j]] = D[j]; S[col[j]]--;
		}
	}
	void restore(int c){
		FOR(i, U, c) FOR(j, L, i){
			U[D[j]] = j; D[U[j]] = j; S[col[j]]++;
		}
		R[L[c]] = c; L[R[c]] = c;
	}
	bool dfs(int d){
		if(R[0] == 0){
			ansd = d;
			return true;
		}
		int c = R[0];
		FOR(i, R, 0){
			if(S[i] < S[c]) c = i;
		}
		remove(c);
		FOR(i, D, c){
			ans[d] = row[i];
			FOR(j, R, i) remove(col[j]);
			if(dfs(d+1)) return true;
			FOR(j, L, i) restore(col[j]);
		}
		restore(c);
		return false;
	}
	bool solve(vector<int> &v){
		v.clear();
		if(!dfs(0)) return false;
		for(int i = 0; i < ansd; i++){
			v.push_back(ans[i]);
		}
		return true;
	}
}dlx;
int t,n,m;
int cnt_r, cnt_c;
int mp[10][10];
int encode(int x, int y, int z){
	return x*81+y*9+z+1;
}
void decode(int code, int &x, int &y, int &z){
    code--;
	z = code%9; code /= 9;
	y = code%9; code /= 9;
	x = code%9;
}
const int SLOT = 0;
const int ROW = 1;
const int COL = 2;
const int SUB = 3;
int main()
{
	cin >> t;
	cnt_r = 9*9*9; cnt_c = 9*9*4;
	while(t--){
		dlx.init(cnt_r, cnt_c);
		for(int i = 0; i < 9; i++){
			for(int j = 0; j < 9; j++){
				cin >> mp[i][j];
			}
		}
		for(int i = 0; i < 9; i++){
			for(int j = 0; j < 9; j++){
				for(int k = 0; k < 9; k++){
					if(mp[i][j] == 0 || mp[i][j] == k+1){
						int r = encode(i, j, k);
						dlx.addNode(r, encode(SLOT, i, j));
						dlx.addNode(r, encode(ROW, i, k));
						dlx.addNode(r, encode(COL, j, k));
						dlx.addNode(r, encode(SUB, (i/3)*3+j/3, k));// 6,9
					}
				}
			}
		}
		vector<int> v;
		dlx.solve(v);
		for(int i = 0; i < v.size(); i++){
			int r, c, k;
			decode(v[i], r, c, k);
			mp[r][c] = k+1;
		}
		for(int i = 0; i < 9; i++){
			for(int j = 0; j < 9; j++){
				if(j) cout << ' ';
				cout << mp[i][j];
			}
			cout << "
";
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Alruddy/p/7707631.html