这种数据结构真的是奇妙啊!!
推荐一篇博客 万仓一黍 的博客
Code:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 250501; 4 int n, m, cnt;//n行m列 cnt是总点数 5 int ans[N]; 6 int row[N];//该点本身所在行 7 int col[N];//该点本身所在列 8 int l[N];//该点水平方向左侧 9 int r[N];//该点水平方向右侧 10 int up[N];//该点竖直方向的上一个点 11 int down[N];//该点竖直方向的下一个点 12 int s[N];//每一列点数 13 int h[N];//每一行表头 14 //这里水平,竖直方向上的列表都是循环的 15 void init(int m) {//初始化 16 for (int i = 0; i <= m; i++) { 17 r[i] = i + 1; 18 l[i] = i - 1; 19 up[i] = down[i] = i; 20 } 21 r[m] = 0; 22 l[0] = m; 23 memset(h, -1, sizeof h); 24 memset(s, 0, sizeof s); 25 cnt = m + 1;//我们在第0行插入了m个点作为辅助点 26 } 27 void insert(int R, int C) {//在R行C列插入点 28 s[C]++; 29 row[cnt] = R; 30 col[cnt] = C; 31 down[cnt] = C;//新节点的下一个点指向该列表头 32 up[cnt] = up[C];//新节点的上一个点是上一个节点 33 down[up[C]] = cnt;//上一个节点的下一个节点是该节点 34 up[C] = cnt;//上一个节点的下一个节点是新节点 35 if (h[R] == -1) { 36 h[R] = r[cnt] = l[cnt] = cnt; 37 } else { 38 r[cnt] = h[R];//新节点的右侧是表头 39 l[cnt] = l[h[R]];//新节点的左侧是表头的左侧即上一个节点 40 r[l[h[R]]] = cnt;//上一个节点的右侧是该新节点 41 l[h[R]] = cnt;//表头的左侧更新为新节点 42 } 43 cnt++;//节点数++ 44 return; 45 } 46 void remove(int c) {//删除c列和c列所有点所在行 47 r[l[c]] = r[c];//c点左面的点的右面的点更新为c点右面的点 48 l[r[c]] = l[c];//c点右面的点的左面的点更新为c点左面的点 49 for (int i = up[c]; i != c; i = up[i]) {//枚举该列的点 50 for (int j = r[i]; j != i; j = r[j]) {//枚举该点所在行并将该行删除 51 down[up[j]] = down[j];//j点上一个点的下一个点是j点的下一个点 52 up[down[j]] = up[j];//j点下一个点的上一个点是j点的上一个点 53 s[col[j]]--;//该列点数-- 54 } 55 } 56 } 57 void resume(int c) {//恢复 58 for (int i = down[c]; i != c; i = down[i]) { 59 for (int j = l[i]; j != i; j = l[j]) { 60 down[up[j]] = j;//该点的上一个点的下一个点是j自己 61 up[down[j]] = j;//该点的下一个点的上一个点是j自己 62 s[col[j]]++;//该列点数++ 63 } 64 } 65 r[l[c]] = c;//c点左面的点的右面的点是c自己 66 l[r[c]] = c;//c点右面的点的左面的点是c自己 67 } 68 bool dance(int deep) {//Dance! 69 if (r[0] == 0) {//如果列表头没有元素,说明合法 70 for (int i = 0; i < deep; i++) {//将ans中的答案输出 71 printf("%d%c", ans[i], i == deep - 1 ? ' ' : ' '); 72 } 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 = up[c]; i != c; i = up[i]) {//枚举该列所有点 83 ans[deep] = row[i];//将答案储存 84 for (int j = r[i]; j != i; j = r[j]) {//将该点所在行所有列删除 85 remove(col[j]); 86 } 87 if (dance(deep + 1)) {//判断是否合法 88 return true; 89 } 90 for (int j = l[i]; j != i; j = l[j]) {//回溯 91 resume(col[j]); 92 } 93 } 94 resume(c);//回溯 95 return false; 96 } 97 int main () { 98 scanf("%d%d", &n, &m); 99 init(m); 100 for (int i = 1; i <= n; i++) { 101 for (int j = 1; j <= m; j++) { 102 int x; 103 scanf("%d", &x); 104 if (x) {//是1就加点 105 insert(i, j); 106 } 107 } 108 } 109 if (!dance(0)) { 110 printf("No Solution! "); 111 } 112 return 0; 113 }