Dancing Link专题

Posted 萌萌的美男子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dancing Link专题相关的知识,希望对你有一定的参考价值。

1、hust 1017 Exact cover (Dancing Links 模板题)

  题意:n*m的单位矩阵。现在要选一些行,使得这些行的集合中每列只出现一个1.

  思路:裸的精确覆盖问题。刷一遍模板。

技术分享
  1 #include <iostream>  
  2 #include <stdio.h>  
  3 #include <string.h>  
  4 //精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1
  5 const int MN = 1005;//最大行数
  6 const int MM = 1005;//最大列数
  7 const int MNN = 1e5 + 5 + MM; //最大点数  
  8 
  9 struct DLX
 10 {
 11     int n, m, si;//n行数m列数si目前有的节点数  
 12     //十字链表组成部分  
 13     int U[MNN], D[MNN], L[MNN], R[MNN], Row[MNN], Col[MNN];
 14     //第i个结点的U向上指针D下L左R右,所在位置Row行Col列  
 15     int H[MN], S[MM]; //记录行的选择情况和列的覆盖情况  
 16     int ansd, ans[MN];
 17     void init(int _n, int _m)  //初始化空表  
 18     {
 19         n = _n;
 20         m = _m;
 21         for (int i = 0; i <= m; i++) //初始化第一横行(表头)  
 22         {
 23             S[i] = 0;
 24             U[i] = D[i] = i;      //目前纵向的链是空的  
 25             L[i] = i - 1;
 26             R[i] = i + 1;         //横向的连起来  
 27         }
 28         R[m] = 0; L[0] = m;
 29         si = m;                 //目前用了前0~m个结点  
 30         for (int i = 1; i <= n; i++)
 31             H[i] = -1;
 32     }
 33     void link(int r, int c)    //插入点(r,c)  
 34     {
 35         ++S[Col[++si] = c];     //si++;Col[si]=c;S[c]++;  
 36         Row[si] = r;//si该结点的行数为r
 37         D[si] = D[c];//向下指向c的下面的第一个结点
 38         U[D[c]] = si;//c的下面的第一个结点的上面为si
 39         U[si] = c;//si的上面为列指针
 40         D[c] = si;//列指针指向的第一个该列中的元素设为si
 41         if (H[r]<0)//如果第r行没有元素
 42             H[r] = L[si] = R[si] = si;
 43         else
 44         {
 45             R[si] = R[H[r]];//si的右边为行指针所指的右边第一个元素
 46             L[R[H[r]]] = si;//行指针所指的右边第一个元素的左侧为si
 47             L[si] = H[r];//si的左侧为行指针
 48             R[H[r]] = si;//行指针的右侧为si
 49         }
 50     }
 51     void remove(int c)        //列表中删掉c列  
 52     {
 53         L[R[c]] = L[c];//表头操作  //c列头指针的右边的元素的左侧指向c列头指针左边的元素
 54         R[L[c]] = R[c];//c列头指针的左边的元素的右侧指向c列头指针右边的元素
 55         for (int i = D[c]; i != c; i = D[i])//遍历该列的所有元素
 56             for (int j = R[i]; j != i; j = R[j])
 57             {//对于该列的某个元素所在的行进行遍历
 58                 U[D[j]] = U[j];//把该元素从其所在列中除去
 59                 D[U[j]] = D[j];
 60                 --S[Col[j]];//该元素所在的列数目减一
 61             }
 62     }
 63     void resume(int c)        //恢复c列  
 64     {
 65         for (int i = U[c]; i != c; i = U[i])//枚举该列元素
 66             for (int j = L[i]; j != i; j = L[j])//枚举该列元素所在的行
 67                 ++S[Col[U[D[j]] = D[U[j]] = j]];//D[U[j]]=j;U[D[j]]=j;S[Col[j]]++;
 68         L[R[c]] = R[L[c]] = c;//c列头指针左右相连
 69     }
 70     bool dance(int d) //选取了d行  
 71     {
 72         if (R[0] == 0)//全部覆盖了  
 73         {
 74             //全覆盖了之后的操作  
 75             ansd = d;
 76             return 1;
 77         }
 78         int c = R[0];//表头结点指向的第一个列
 79         for (int i = R[0]; i != 0; i = R[i])//枚举列头指针
 80             if (S[i]<S[c])//找到列中元素个数最少的
 81                 c = i;
 82         remove(c);//将该列删去
 83         for (int i = D[c]; i != c; i = D[i])
 84         {//枚举该列的元素
 85             ans[d] = Row[i];//记录该列元素的行
 86             for (int j = R[i]; j != i; j = R[j])
 87                 remove(Col[j]);//将该列的某个元素的行上的元素所在的列都删去
 88             if (dance(d + 1))
 89                 return 1;
 90             for (int j = L[i]; j != i; j = L[j])
 91                 resume(Col[j]);
 92         }
 93         resume(c);
 94         return 0;
 95     }
 96 }dlx;
 97 
 98 int main()
 99 {
100     int n, m;
101     while (scanf("%d%d", &n, &m) != EOF)
102     {
103         dlx.init(n, m);
104         for (int i = 1; i <= n; i++)
105         {//共n列
106             int k;
107             scanf("%d", &k);//每列中含1的个数
108             while (k--)
109             {
110                 int cc;
111                 scanf("%d", &cc);//输入其所在的列
112                 dlx.link(i, cc);//链接
113             }
114         }
115         dlx.ansd = -1;
116         if (dlx.dance(0))
117         {
118             printf("%d", dlx.ansd);
119             for (int i = 0; i<dlx.ansd; i++)
120                 printf(" %d", dlx.ans[i]);
121             printf("\n");
122         }
123         else
124             printf("NO\n");
125     }
126     return 0;
127 }
View Code

2、ZOJ 3209 Treasure Map

  题意:给出一些矩形,问最少需要多少个矩形可以把指定的一块区域覆盖。

  思路:把每个矩形块看成行,把指定区域分成1*1的单元格,所有的单元格看成列。

技术分享
  1 #include <iostream>  
  2 #include <stdio.h>  
  3 #include <string.h>
  4 #include<algorithm>
  5 //精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1
  6 const int MN = 1005;//最大行数
  7 const int MM = 1005;//最大列数
  8 const int MNN = 1e5 + 5 + MM; //最大点数  
  9 
 10 struct DLX
 11 {
 12     int n, m, si;//n行数m列数si目前有的节点数  
 13                  //十字链表组成部分  
 14     int U[MNN], D[MNN], L[MNN], R[MNN], Row[MNN], Col[MNN];
 15     //第i个结点的U向上指针D下L左R右,所在位置Row行Col列  
 16     int H[MN], S[MM]; //记录行的选择情况和列的覆盖情况  
 17     int ansd, ans[MN];
 18     void init(int _n, int _m)  //初始化空表  
 19     {
 20         n = _n;
 21         m = _m;
 22         for (int i = 0; i <= m; i++) //初始化第一横行(表头)  
 23         {
 24             S[i] = 0;
 25             U[i] = D[i] = i;      //目前纵向的链是空的  
 26             L[i] = i - 1;
 27             R[i] = i + 1;         //横向的连起来  
 28         }
 29         R[m] = 0; L[0] = m;
 30         si = m;                 //目前用了前0~m个结点  
 31         for (int i = 1; i <= n; i++)
 32             H[i] = -1;
 33     }
 34     void link(int r, int c)    //插入点(r,c)  
 35     {
 36         ++S[Col[++si] = c];     //si++;Col[si]=c;S[c]++;  
 37         Row[si] = r;//si该结点的行数为r
 38         D[si] = D[c];//向下指向c的下面的第一个结点
 39         U[D[c]] = si;//c的下面的第一个结点的上面为si
 40         U[si] = c;//si的上面为列指针
 41         D[c] = si;//列指针指向的第一个该列中的元素设为si
 42         if (H[r]<0)//如果第r行没有元素
 43             H[r] = L[si] = R[si] = si;
 44         else
 45         {
 46             R[si] = R[H[r]];//si的右边为行指针所指的右边第一个元素
 47             L[R[H[r]]] = si;//行指针所指的右边第一个元素的左侧为si
 48             L[si] = H[r];//si的左侧为行指针
 49             R[H[r]] = si;//行指针的右侧为si
 50         }
 51     }
 52     void remove(int c)        //列表中删掉c列  
 53     {
 54         L[R[c]] = L[c];//表头操作  //c列头指针的右边的元素的左侧指向c列头指针左边的元素
 55         R[L[c]] = R[c];//c列头指针的左边的元素的右侧指向c列头指针右边的元素
 56         for (int i = D[c]; i != c; i = D[i])//遍历该列的所有元素
 57             for (int j = R[i]; j != i; j = R[j])
 58             {//对于该列的某个元素所在的行进行遍历
 59                 U[D[j]] = U[j];//把该元素从其所在列中除去
 60                 D[U[j]] = D[j];
 61                 --S[Col[j]];//该元素所在的列数目减一
 62             }
 63     }
 64     void resume(int c)        //恢复c列  
 65     {
 66         for (int i = U[c]; i != c; i = U[i])//枚举该列元素
 67             for (int j = L[i]; j != i; j = L[j])//枚举该列元素所在的行
 68                 ++S[Col[U[D[j]] = D[U[j]] = j]];//D[U[j]]=j;U[D[j]]=j;S[Col[j]]++;
 69         L[R[c]] = R[L[c]] = c;//c列头指针左右相连
 70     }
 71     bool dance(int d) //选取了d行  
 72     {
 73         if (ansd != -1 && ansd < d)return 0;
 74         if (R[0] == 0)//全部覆盖了
 75         {
 76             //全覆盖了之后的操作  
 77             if(ansd==-1)ansd = d;
 78             else if (d < ansd) ansd = d;
 79             return 1;
 80         }
 81         int c = R[0];//表头结点指向的第一个列
 82         for (int i = R[0]; i != 0; i = R[i])//枚举列头指针
 83             if (S[i]<S[c])//找到列中元素个数最少的
 84                 c = i;
 85         remove(c);//将该列删去
 86         for (int i = D[c]; i != c; i = D[i])
 87         {//枚举该列的元素
 88             ans[d] = Row[i];//记录该列元素的行
 89             for (int j = R[i]; j != i; j = R[j])
 90                 remove(Col[j]);//将该列的某个元素的行上的元素所在的列都删去
 91             (dance(d + 1));
 92             for (int j = L[i]; j != i; j = L[j])
 93                 resume(Col[j]);
 94         }
 95         resume(c);
 96         return 0;
 97     }
 98 }dlx;
 99 
100 int main()
101 {
102     int n, m,p;
103     int t;
104     scanf("%d", &t);
105     while (t--)
106     {
107         scanf("%d%d%d", &n, &m, &p);
108         dlx.init(p, n*m);//将块当成行,所有的单元格看成列
109         for (int pp = 1; pp <= p; pp++)
110         {
111             int x1, x2, y1, y2;
112             scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
113             for (int i = x1 + 1; i <= x2; i++)
114             {
115                 for (int j = y1 + 1; j <= y2; j++)
116                 {
117                     dlx.link(pp, (i - 1)*m + j);
118                 }
119             }
120         }
121         dlx.ansd = -1;
122         dlx.dance(0);
123         printf("%d\n", dlx.ansd);
124     }
125     return 0;
126 }
View Code

 

以上是关于Dancing Link专题的主要内容,如果未能解决你的问题,请参考以下文章

使用改良版多值覆盖Dancing link X (舞蹈链)求解aquarium游戏

使用修改版Dancing link X (舞蹈链)求解aquarium游戏

16个必备的JavaScript代码片段

DLL(Dynamic Link Libraries)专题

数据结构专题-学习笔记:Link Cut Tree 动态树

hihoCoder #1321 : 搜索五?数独 (Dancing Links ,精确覆盖)