[poj3074]Sudoku(舞蹈链)
题目链接:http://poj.org/problem?id=3074
舞蹈链精确覆盖的经典题目,一个数独每个位置的要求,可以得到以下四个约束
1.每个位置有且只有一个数字
2.每个位置的数字在一行只能出现一次
3.每个位置的数字在一列只能出现一次
4.每个位置的数字在一个宫格内只能出现一次
然后针对每个位置可以建立舞蹈链了
前81列,为1条件的约束
82-162列,为2条件的约束
163-243列,为3条件的约束
244-324列,为4条件的约束
则舞蹈链行数为确定的点数+未确定的点数*9,列数为324。
如果i行j列为数k,对应行则在(i - 1) 9 + j列,(i - 1) 9 + k + 81列, (j - 1) 9 + k + 162列,(((i - 1) / 3) 3 + ((j + 2) / 3) - 1)* 9 + k + 243列建立。
如果i行j列为未知数,则k为1-9,对于每个k都应该建立行列关系。
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 #include<string> 6 #include<algorithm> 7 #include<set> 8 #include<cstdlib> 9 #include<map> 10 #include<queue> 11 #include<cmath> 12 using namespace std; 13 typedef long long ll; 14 const int MN = 1010; 15 const int MM = 500; 16 const int MNN = MN * MM + 10; 17 struct DLX { 18 19 int n, m, size;//一共n行m列,size个节点 20 int U[MNN], D[MNN], L[MNN], R[MNN], row[MNN], col[MNN];//第i个节点的上U下D左L右R,所在位置row行col列 21 int H[MNN], S[MM];//H数组记录行选择指针,S数组记录覆盖个数 22 int ansd, ans[MN];//res记录行个数,ans数组记录可行解 23 void init(int x, int y) {//初始化空表 24 n = x, m = y; 25 for (int i = 0; i <= m; ++i) {//其中0节点作为head节点,其他作为列首节点 26 U[i] = D[i] = i; 27 L[i] = i - 1; 28 R[i] = i + 1; 29 } 30 R[m] = 0; L[0] = m; 31 size = m; 32 memset(S, 0, sizeof(S)); 33 memset(H, -1, sizeof(H)); 34 } 35 void Link(int r, int c) { 36 size++; row[size] = r; col[size] = c; S[c]++;//节点数加一,设置s节点所处位置,以及S列覆盖个数加一 37 D[size] = D[c]; U[D[c]] = size;//将s节点插入对应列中 38 U[size] = c; D[c] = size; 39 if (H[r] < 0)//如果该行没有元素,H[r]标记该行起始节点 40 H[r] = L[size] = R[size] = size; 41 else {//将该节点插入该行第一个节点后面 42 R[size] = R[H[r]]; 43 L[R[H[r]]] = size; 44 L[size] = H[r]; 45 R[H[r]] = size; 46 } 47 } 48 //精确覆盖 49 void remove(int c) {//删除c列 50 L[R[c]] = L[c]; R[L[c]] = R[c]; 51 //删除该列上的元素对应的行 52 for (int i = D[c]; i != c; i = D[i]) {//枚举该列元素 53 for (int j = R[i]; j != i; j = R[j]) {//枚举列的某个元素所在行遍历 54 U[D[j]] = U[j]; 55 D[U[j]] = D[j]; 56 //将该列上的S数组减一 57 --S[col[j]]; 58 } 59 } 60 } 61 void resume(int c) {//恢复c列 62 for (int i = U[c]; i != c; i = U[i]) {//枚举该列元素 63 for (int j = L[i]; j != i; j = L[j]) { 64 U[D[j]] = j; D[U[j]] = j; 65 ++S[col[j]]; 66 } 67 } 68 L[R[c]] = c; R[L[c]] = c; 69 } 70 bool dancing(int deep) { 71 if (R[0] == 0) {//当矩阵为空时,说明找到一个可行解,算法终止 72 ansd = deep; 73 return true; 74 } 75 int c = R[0];//找到节点数最少的列,枚举这列上的所有行 76 for (int i = R[0]; i != 0; i = R[i]) { 77 if (S[i] < S[c]) { 78 c = i; 79 } 80 } 81 remove(c);//删除节点数最少的列 82 for (int i = D[c]; i != c; i = D[i]) { 83 ans[deep] = row[i];//将行r放入当前解 84 for (int j = R[i]; j != i; j = R[j])//行上节点对应的列上进行删除 85 remove(col[j]); 86 //进入下一层 87 if (dancing(deep + 1))return true; 88 //对行上的节点对应的列进行恢复 89 for (int j = L[i]; j != i; j = L[j]) 90 resume(col[j]); 91 } 92 //恢复节点数最少列 93 resume(c); 94 return false; 95 } 96 }; 97 DLX dzl; 98 int mp[15][15]; 99 int x[1010]; 100 int y[1010]; 101 int kk[1010]; 102 int main() { 103 char s[100]; 104 while (cin >> s) { 105 if (strcmp(s, "end") == 0) 106 break; 107 for (int i = 0; i < 81; i++) { 108 int xx = i / 9; 109 int yy = i % 9; 110 if (s[i] == '.') 111 mp[xx + 1][yy + 1] = 0; 112 else 113 mp[xx + 1][yy + 1] = s[i] - '0'; 114 } 115 dzl.init(800, 324); 116 int len = 1; 117 for (int i = 1; i <= 9; i++) { 118 for (int j = 1; j <= 9; j++) { 119 if (mp[i][j] == 0) { 120 for (int k = 1; k <= 9; k++) { 121 dzl.Link(len, (i - 1) * 9 + j); 122 dzl.Link(len, (i - 1) * 9 + k + 81); 123 dzl.Link(len, (j - 1) * 9 + k + 162); 124 dzl.Link(len, (((i - 1) / 3) * 3 + ((j + 2) / 3) - 1) * 9 + k + 243); 125 x[len] = i, y[len] = j, kk[len] = k; 126 len++; 127 } 128 } 129 else { 130 int k = mp[i][j]; 131 dzl.Link(len, (i - 1) * 9 + j); 132 dzl.Link(len, (i - 1) * 9 + k + 81); 133 dzl.Link(len, (j - 1) * 9 + k + 162); 134 dzl.Link(len, (((i - 1) / 3) * 3 + ((j + 2) / 3) - 1) * 9 + k + 243); 135 x[len] = i, y[len] = j, kk[len] = k; 136 len++; 137 } 138 } 139 } 140 dzl.dancing(0); 141 for (int i = 0; i < dzl.ansd; i++) { 142 int r = dzl.ans[i]; 143 mp[x[r]][y[r]] = kk[r]; 144 } 145 for (int i = 1; i <= 9; i++) { 146 for (int j = 1; j <= 9; j++) { 147 printf("%d", mp[i][j]); 148 } 149 } 150 puts(""); 151 } 152 }