[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 }
原文地址:https://www.cnblogs.com/sainsist/p/11147739.html