舞蹈链

LuoguP4929

这种数据结构真的是奇妙啊!!

推荐一篇博客 万仓一黍 的博客

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 }
View Code
原文地址:https://www.cnblogs.com/Sundial/p/11830580.html