一些链接:
http://www.cnblogs.com/-sunshine/p/3358922.html
http://www.cnblogs.com/grenet/p/3145800.html
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(" "); 122 } 123 else 124 printf("NO "); 125 } 126 return 0; 127 }
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 ", dlx.ansd); 124 } 125 return 0; 126 }
3、HDU 2295 Radar
题意:给出n个城市,给出m个雷达(都是坐标),要求在使用不超过k个雷达的情况下,并且保证覆盖所有的城市,使得每个雷达的覆盖半径最小。
思路:DLK重复覆盖模板。
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 //重复覆盖:找到一些行,使得这些行的集合中每列至少有一个1 5 const int MN = 55;//最大行数 6 const int MM = 55;//最大列数 7 const int MNN = MM*MN+MM+MN+10; //最大点数 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 /*重复覆盖*//*c为元素编号,而非列号*//*即删除该元素所在的列,包括它自身和列头指针*/ 63 for (int i = D[c]; i != c; i = D[i]) 64 { 65 L[R[i]] = L[i], R[L[i]] = R[i]; 66 } 67 } 68 void resume(int c)//恢复c列 69 { 70 //for (int i = U[c]; i != c; i = U[i])//枚举该列元素 71 // for (int j = L[i]; j != i; j = L[j])//枚举该列元素所在的行 72 // ++S[Col[U[D[j]] = D[U[j]] = j]];//D[U[j]]=j;U[D[j]]=j;S[Col[j]]++; 73 //L[R[c]] = R[L[c]] = c;//c列头指针左右相连 74 /*重复覆盖*/ 75 for (int i = U[c]; i != c; i = U[i]) 76 { 77 L[R[i]] = R[L[i]] = i; 78 } 79 } 80 int f_check()//精确覆盖区估算剪枝 81 { 82 /* 83 强剪枝。这个 剪枝利用的思想是A*搜索中的估价函数。即,对于当前的递归深度K下的矩阵,估计其最好情况下(即最少还需要多少步)才能出解。也就是,如果将能够覆盖当 前列的所有行全部选中,去掉这些行能够覆盖到的列,将这个操作作为一层深度。重复此操作直到所有列全部出解的深度是多少。如果当前深度加上这个估价函数返 回值,其和已然不能更优(也就是已经超过当前最优解),则直接返回,不必再搜。 84 */ 85 int vis[MNN]; 86 memset(vis, 0, sizeof(vis)); 87 int ret = 0; 88 for (int c = R[0]; c != 0; c = R[c]) vis[c] = true; 89 for (int c = R[0]; c != 0; c = R[c]) 90 if (vis[c]) 91 { 92 ret++; 93 vis[c] = false; 94 for (int i = D[c]; i != c; i = D[i]) 95 for (int j = R[i]; j != i; j = R[j]) 96 vis[Col[j]] = false; 97 } 98 return ret; 99 } 100 bool dance(int d,int limit) //选取了d行,limit为限制选取的最大行数 101 { 102 /*重复覆盖 103 1、如果矩阵为空,得到结果,返回 104 2、从矩阵中选择一列,以选取最少元素的列为优化方式 105 3、删除该列及其覆盖的行 106 4、对该列的每一行元素:删除一行及其覆盖的列, 107 5、进行下一层搜索,如果成功则返回 108 6、恢复现场,跳至4 109 7、恢复所选择行 110 */ 111 if (d > limit)return false; 112 if (d + f_check() > limit)return false; 113 if (R[0] == 0)//全部覆盖了 114 { 115 //全覆盖了之后的操作 116 ansd = d; 117 return true; 118 } 119 int c = R[0];//表头结点指向的第一个列 120 for (int i = R[0]; i != 0; i = R[i])//枚举列头指针 121 if (S[i]<S[c])//找到列中元素个数最少的 122 c = i; 123 //remove(c);//将该列删去(精确覆盖) 124 for (int i = D[c]; i != c; i = D[i]) 125 {//枚举该列的元素 126 ans[d] = Row[i];//记录该列元素的行 127 remove(i);//新增(重复覆盖) 128 for (int j = R[i]; j != i; j = R[j]) 129 remove(j);//remove(Col[j])(精确覆盖) 130 if (dance(d + 1, limit)) return true; 131 for (int j = L[i]; j != i; j = L[j]) 132 resume(j);//resume(Col[j])(精确覆盖) 133 resume(i);//新增(重复覆盖) 134 } 135 //resume(c);(精确覆盖) 136 return false; 137 } 138 }dlx; 139 struct node 140 { 141 int x; 142 int y; 143 }citys[55],radar[55]; 144 double dis[55][55]; 145 int main() 146 { 147 int n, m,k; 148 int t; 149 scanf("%d", &t); 150 151 while (t--) 152 { 153 double l = 0, r=0,maxx=0,maxy=0; 154 scanf("%d%d%d", &n, &m, &k); 155 for (int i = 1; i <= n; i++) 156 { 157 scanf("%d%d", &citys[i].x, &citys[i].y); 158 if (citys[i].x + citys[i].y > maxx + maxy) 159 { 160 maxx = citys[i].x, maxy = citys[i].y; 161 } 162 } 163 for (int i = 1; i <= m; i++) 164 { 165 scanf("%d%d", &radar[i].x, &radar[i].y); 166 if (radar[i].x + radar[i].y > maxx + maxy) 167 { 168 maxx = radar[i].x, maxy = radar[i].y; 169 } 170 } 171 for (int i = 1; i <= m; i++) 172 { 173 for (int j = 1; j <= n; j++) 174 { 175 dis[i][j] = sqrt((radar[i].x - citys[j].x)*(radar[i].x - citys[j].x) + (radar[i].y - citys[j].y)*(radar[i].y - citys[j].y)); 176 } 177 } 178 r = sqrt(maxx*maxx + maxy*maxy); 179 while (r - l > 1e-7) 180 { 181 double mid = (l + r) / 2; 182 dlx.init(m, n); 183 for (int i = 1; i <= m; i++) 184 { 185 for (int j = 1; j <= n; j++) 186 { 187 if (dis[i][j] <= mid) dlx.link(i, j); 188 } 189 } 190 dlx.ansd = -1; 191 if (dlx.dance(0,k)) r = mid; 192 else l = mid; 193 } 194 printf("%.6lf ", l); 195 } 196 return 0; 197 }
4、FZU 1686 神龙的难题
题意:有个n*m的矩形,每次可以把n1*m1小矩形范围内的敌人消灭,最少的次数。
思路:DLK重复覆盖模板,枚举所有的小矩形,设为行,把所有敌人编号,记为列。
1 #include<iostream> 2 #include<cstdio> 3 #include<memory.h> 4 #include<algorithm> 5 using namespace std; 6 //重复覆盖:找到一些行,使得这些行的集合中每列至少有一个1 7 const int MN =250;//最大行数 8 const int MM = 250;//最大列数 9 const int MNN = MM*MN; //最大点数 10 11 struct DLX 12 { 13 int n, m, si;//n行数m列数si目前有的节点数 14 //十字链表组成部分 15 int U[MNN], D[MNN], L[MNN], R[MNN], Row[MNN], Col[MNN]; 16 //第i个结点的U向上指针D下L左R右,所在位置Row行Col列 17 int H[MN], S[MM]; //记录行的选择情况和列的覆盖情况 18 int ansd, ans[MN]; 19 void init(int _n, int _m) //初始化空表 20 { 21 n = _n; 22 m = _m; 23 for (int i = 0; i <= m; i++) //初始化第一横行(表头) 24 { 25 S[i] = 0; 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 si = m; //目前用了前0~m个结点 32 for (int i = 1; i <= n; i++) 33 H[i] = -1; 34 } 35 void link(int r, int c) //插入点(r,c) 36 { 37 ++S[Col[++si] = c]; //si++;Col[si]=c;S[c]++; 38 Row[si] = r;//si该结点的行数为r 39 D[si] = D[c];//向下指向c的下面的第一个结点 40 U[D[c]] = si;//c的下面的第一个结点的上面为si 41 U[si] = c;//si的上面为列指针 42 D[c] = si;//列指针指向的第一个该列中的元素设为si 43 if (H[r]<0)//如果第r行没有元素 44 H[r] = L[si] = R[si] = si; 45 else 46 { 47 R[si] = R[H[r]];//si的右边为行指针所指的右边第一个元素 48 L[R[H[r]]] = si;//行指针所指的右边第一个元素的左侧为si 49 L[si] = H[r];//si的左侧为行指针 50 R[H[r]] = si;//行指针的右侧为si 51 } 52 } 53 void remove(int c) //列表中删掉c列 54 { 55 //L[R[c]] = L[c];//表头操作 //c列头指针的右边的元素的左侧指向c列头指针左边的元素 56 //R[L[c]] = R[c];//c列头指针的左边的元素的右侧指向c列头指针右边的元素 57 //for (int i = D[c]; i != c; i = D[i])//遍历该列的所有元素 58 // for (int j = R[i]; j != i; j = R[j]) 59 // {//对于该列的某个元素所在的行进行遍历 60 // U[D[j]] = U[j];//把该元素从其所在列中除去 61 // D[U[j]] = D[j]; 62 // --S[Col[j]];//该元素所在的列数目减一 63 // } 64 /*重复覆盖*//*c为元素编号,而非列号*//*即删除该元素所在的列,包括它自身和列头指针*/ 65 for (int i = D[c]; i != c; i = D[i]) 66 { 67 L[R[i]] = L[i], R[L[i]] = R[i]; 68 } 69 } 70 71 void resume(int c)//恢复c列 72 { 73 //for (int i = U[c]; i != c; i = U[i])//枚举该列元素 74 // for (int j = L[i]; j != i; j = L[j])//枚举该列元素所在的行 75 // ++S[Col[U[D[j]] = D[U[j]] = j]];//D[U[j]]=j;U[D[j]]=j;S[Col[j]]++; 76 //L[R[c]] = R[L[c]] = c;//c列头指针左右相连 77 /*重复覆盖*/ 78 for (int i = U[c]; i != c; i = U[i]) 79 { 80 L[R[i]] = R[L[i]] = i; 81 } 82 } 83 84 85 bool vis[MNN]; 86 int f_check()//精确覆盖区估算剪枝 87 { 88 /* 89 强剪枝。这个 剪枝利用的思想是A*搜索中的估价函数。即,对于当前的递归深度K下的矩阵,估计其最好情况下(即最少还需要多少步)才能出解。也就是,如果将能够覆盖当 前列的所有行全部选中,去掉这些行能够覆盖到的列,将这个操作作为一层深度。重复此操作直到所有列全部出解的深度是多少。如果当前深度加上这个估价函数返 回值,其和已然不能更优(也就是已经超过当前最优解),则直接返回,不必再搜。 90 */ 91 92 int ret = 0; 93 for (int c = R[0]; c != 0; c = R[c]) vis[c] = true; 94 for (int c = R[0]; c != 0; c = R[c]) 95 if (vis[c]) 96 { 97 ret++; 98 vis[c] = false; 99 for (int i = D[c]; i != c; i = D[i]) 100 for (int j = R[i]; j != i; j = R[j]) 101 vis[Col[j]] = false; 102 } 103 return ret; 104 } 105 void dance(int d) //选取了d行 106 { 107 /*重复覆盖 108 1、如果矩阵为空,得到结果,返回 109 2、从矩阵中选择一列,以选取最少元素的列为优化方式 110 3、删除该列及其覆盖的行 111 4、对该列的每一行元素:删除一行及其覆盖的列, 112 5、进行下一层搜索,如果成功则返回 113 6、恢复现场,跳至4 114 7、恢复所选择行 115 */ 116 if (d + f_check() >=ansd)return; 117 if (R[0] == 0)//全部覆盖了 118 { 119 //全覆盖了之后的操作 120 if(ansd > d) ansd=d; 121 return; 122 } 123 int c = R[0];//表头结点指向的第一个列 124 for (int i = R[0]; i != 0; i = R[i])//枚举列头指针 125 if (S[i]<S[c])//找到列中元素个数最少的 126 c = i; 127 //remove(c);//将该列删去(精确覆盖) 128 for (int i = D[c]; i != c; i = D[i]) 129 {//枚举该列的元素 130 ans[d] = Row[i];//记录该列元素的行 131 remove(i);//新增(重复覆盖) 132 for (int j = R[i]; j != i; j = R[j]) 133 remove(j);//remove(Col[j])(精确覆盖) 134 dance(d + 1); 135 for (int j = L[i]; j != i; j = L[j]) 136 resume(j);//resume(Col[j])(精确覆盖) 137 resume(i);//新增(重复覆盖) 138 } 139 //resume(c);(精确覆盖) 140 } 141 }dlx; 142 int mp[20][20]; 143 int main() 144 { 145 int n, m,n1,m1; 146 while (~scanf("%d%d",&n,&m)) 147 { 148 int cnt = 0; 149 for (int i = 1; i <= n; i++) 150 { 151 for (int j = 1; j <= m; j++) 152 { 153 scanf("%d", &mp[i][j]); 154 if (mp[i][j]) mp[i][j] = ++cnt; 155 } 156 } 157 scanf("%d%d", &n1, &m1); 158 dlx.init(n*m,cnt); 159 cnt = 1; 160 for (int i = 1; i <=n; i++) 161 { 162 for (int j = 1; j <=m; j++) 163 { 164 for (int x = 0; x <n1&&i+x<=n; x++) 165 { 166 for (int z = 0; z < m1&&z+j<=m; z++) 167 { 168 if (mp[i+x][j+z]) dlx.link(cnt, mp[i+x][j+z]); 169 } 170 } 171 cnt++; 172 } 173 } 174 dlx.ansd = 0x3f3f3f3f; 175 dlx.dance(0); 176 printf("%d ", dlx.ansd); 177 } 178 return 0; 179 }
5、poj 1084 Square Destroyer
题意:给出由火柴构成的网格,现在在已经删去若干根火柴后,问最少还需要删去多少火柴,使得网格中的所有正方形被破坏。
思路:DLK重复覆盖,把每根火柴作为行,每个正方形看成列,如果某根火柴可以构成该正方形的一条边,则link.
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #define N 60//55个格子 6 #define M 70//60个火柴 7 #define NN 300//每个格子4根火柴,算上辅助是5 8 #define inf 0x3f3f3f3f 9 using namespace std; 10 /*60火柴,25格*/ 11 int ans; 12 struct DLX 13 { 14 int U[NN], D[NN], L[NN], R[NN], C[NN]; 15 int H[M], T[N], cnt; 16 inline void init() 17 { 18 cnt = 0; 19 memset(U, 0, sizeof(U)); 20 memset(D, 0, sizeof(D)); 21 memset(L, 0, sizeof(L)); 22 memset(R, 0, sizeof(R)); 23 memset(C, 0, sizeof(C)); 24 memset(H, 0, sizeof(H)); 25 memset(T, 0, sizeof(T)); 26 } 27 inline void newnode(int x, int y) 28 { 29 C[++cnt] = y; T[y]++; 30 31 if (!H[x])H[x] = L[cnt] = R[cnt] = cnt; 32 else L[cnt] = H[x], R[cnt] = R[H[x]]; 33 R[H[x]] = L[R[H[x]]] = cnt, H[x] = cnt; 34 35 U[cnt] = U[y], D[cnt] = y; 36 U[y] = D[U[y]] = cnt; 37 } 38 int id[N][N][5], eid[2][7][7]; 39 bool destroy[N], map[N][M]; 40 inline void build() 41 { 42 init(); 43 int i, j, k, n, m; 44 int nmrx = 0, npon = 0; 45 scanf("%d%d", &n, &m); 46 memset(destroy, 0, sizeof(destroy)); 47 memset(map, 0, sizeof(map)); 48 for (k = 0; k<n; k++)for (i = 1; i + k <= n; i++)for (j = 1; j + k <= n; j++)id[k][i][j] = ++nmrx; 49 for (i = 1; i <= n; i++) 50 { 51 for (j = 1; j <= n; j++)eid[0][i][j] = ++npon; 52 for (j = 1; j <= n + 1; j++)eid[1][i][j] = ++npon; 53 } 54 for (i = 1; i <= n; i++)eid[0][n + 1][i] = ++npon; 55 for (k = 0; k<n; k++) 56 for (i = 1; i + k <= n; i++) 57 for (j = 1; j + k <= n; j++) 58 { 59 int A = id[k][i][j], temp; 60 for (temp = j; temp <= j + k; temp++)map[A][eid[0][i][temp]] = map[A][eid[0][i + k + 1][temp]] = 1; 61 for (temp = i; temp <= i + k; temp++)map[A][eid[1][temp][j]] = map[A][eid[1][temp][j + k + 1]] = 1; 62 } 63 for (i = 1; i <= m; i++) 64 { 65 scanf("%d", &k); 66 for (j = 1; j <= nmrx; j++)if (map[j][k])destroy[j] = 1; 67 } 68 for (i = 1; i <= nmrx; i++)if (!destroy[i]) 69 { 70 U[i] = D[i] = i; 71 L[i] = L[0], R[i] = 0; 72 L[0] = R[L[0]] = i; 73 } 74 cnt = nmrx; 75 for (i = 1; i <= nmrx; i++) 76 if (!destroy[i]) 77 for (j = 1; j <= npon; j++) 78 if (map[i][j]) 79 newnode(j, i); 80 } 81 inline void remove(int x) 82 { 83 int i = x; 84 do 85 { 86 R[L[i]] = R[i]; 87 L[R[i]] = L[i]; 88 i = D[i]; 89 } while (i != x); 90 } 91 inline void resume(int x) 92 { 93 int i = x; 94 do 95 { 96 R[L[i]] = i; 97 L[R[i]] = i; 98 i = U[i]; 99 } while (i != x); 100 } 101 inline void dfs(int x) 102 { 103 if (x >= ans)return; 104 if (!R[0]) 105 { 106 ans = x; 107 return; 108 } 109 int S = R[0], W = T[S], i, j; 110 int del[N], num; 111 for (i = R[S]; i; i = R[i])if (T[i]<W) 112 { 113 W = T[i]; 114 S = i; 115 } 116 remove(S); 117 for (i = D[S]; i != S; i = D[i]) 118 { 119 del[num = 1] = R[i]; 120 for (j = R[R[i]]; j != R[i]; j = R[j])del[++num] = j; 121 for (j = 1; j <= num; j++)remove(del[j]); 122 dfs(x + 1); 123 for (j = num; j; j--)resume(del[j]); 124 } 125 resume(S); 126 return; 127 } 128 }dlx; 129 int main() 130 { 131 int g; 132 scanf("%d", &g); 133 while (g--) 134 { 135 ans = inf; 136 dlx.build(); 137 dlx.dfs(0); 138 printf("%d ", ans); 139 } 140 return 0; 141 }
6、poj 3074 Sudoku
题意:解9*9的数独。
思路:主要建图,每个不确定格子至多可以填9种数字,一共9*9*9行,对于每个格子,其自身、所在行、所在列、所在块均存在约束,共9*9*4列。
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 //精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 5 const int MN = 9*9*9+10;//最大行数,共有9*9个格子,每个格子可以放1~9 6 const int MM = 9*9+9*9+9*9+9*9+100;//最大列数 7 //用第(i - 1) * 9 + j列为1表示i行j列的已经填数。一共占用81列。 8 //用81 + (i - 1) * 9 + v列表示第i行已经有v这个值。一共占用81列。 9 //用162 + (j - 1) * 9 + v列表示第j列已经有v这个值。一共占用81列。 10 //用243 + (3 * ((i - 1) / 3) + (j + 2) / 3 - 1) + v列表示第3*((i - 1) / 3) + (j + 2) / 3宫格已经有v这个值。一共占用81列。 11 const int MNN = MN*MM; //最大点数 12 13 struct DLX 14 { 15 int n, m, si;//n行数m列数si目前有的节点数 16 //十字链表组成部分 17 int U[MNN], D[MNN], L[MNN], R[MNN], Row[MNN], Col[MNN]; 18 //第i个结点的U向上指针D下L左R右,所在位置Row行Col列 19 int H[MN], S[MM]; //记录行的选择情况和列的覆盖情况 20 int ansd, ans[MN]; 21 void init(int _n, int _m) //初始化空表 22 { 23 n = _n; 24 m = _m; 25 for (int i = 0; i <= m; i++) //初始化第一横行(表头) 26 { 27 S[i] = 0; 28 U[i] = D[i] = i; //目前纵向的链是空的 29 L[i] = i - 1; 30 R[i] = i + 1; //横向的连起来 31 } 32 R[m] = 0; L[0] = m; 33 si = m; //目前用了前0~m个结点 34 for (int i = 1; i <= n; i++) 35 H[i] = -1; 36 } 37 void link(int r, int c) //插入点(r,c) 38 { 39 ++S[Col[++si] = c]; //si++;Col[si]=c;S[c]++; 40 Row[si] = r;//si该结点的行数为r 41 D[si] = D[c];//向下指向c的下面的第一个结点 42 U[D[c]] = si;//c的下面的第一个结点的上面为si 43 U[si] = c;//si的上面为列指针 44 D[c] = si;//列指针指向的第一个该列中的元素设为si 45 if (H[r]<0)//如果第r行没有元素 46 H[r] = L[si] = R[si] = si; 47 else 48 { 49 R[si] = R[H[r]];//si的右边为行指针所指的右边第一个元素 50 L[R[H[r]]] = si;//行指针所指的右边第一个元素的左侧为si 51 L[si] = H[r];//si的左侧为行指针 52 R[H[r]] = si;//行指针的右侧为si 53 } 54 } 55 void remove(int c) //列表中删掉c列 56 { 57 L[R[c]] = L[c];//表头操作 //c列头指针的右边的元素的左侧指向c列头指针左边的元素 58 R[L[c]] = R[c];//c列头指针的左边的元素的右侧指向c列头指针右边的元素 59 for (int i = D[c]; i != c; i = D[i])//遍历该列的所有元素 60 for (int j = R[i]; j != i; j = R[j]) 61 {//对于该列的某个元素所在的行进行遍历 62 U[D[j]] = U[j];//把该元素从其所在列中除去 63 D[U[j]] = D[j]; 64 --S[Col[j]];//该元素所在的列数目减一 65 } 66 } 67 void resume(int c) //恢复c列 68 { 69 for (int i = U[c]; i != c; i = U[i])//枚举该列元素 70 for (int j = L[i]; j != i; j = L[j])//枚举该列元素所在的行 71 ++S[Col[U[D[j]] = D[U[j]] = j]];//D[U[j]]=j;U[D[j]]=j;S[Col[j]]++; 72 L[R[c]] = R[L[c]] = c;//c列头指针左右相连 73 } 74 bool dance(int d) //选取了d行 75 { 76 if (R[0] == 0)//全部覆盖了 77 { 78 //全覆盖了之后的操作 79 ansd = d; 80 return 1; 81 } 82 int c = R[0];//表头结点指向的第一个列 83 for (int i = R[0]; i != 0; i = R[i])//枚举列头指针 84 if (S[i]<S[c])//找到列中元素个数最少的 85 c = i; 86 remove(c);//将该列删去 87 for (int i = D[c]; i != c; i = D[i]) 88 {//枚举该列的元素 89 ans[d] = Row[i];//记录该列元素的行 90 for (int j = R[i]; j != i; j = R[j]) 91 remove(Col[j]);//将该列的某个元素的行上的元素所在的列都删去 92 if (dance(d + 1)) 93 return 1; 94 for (int j = L[i]; j != i; j = L[j]) 95 resume(Col[j]); 96 } 97 resume(c); 98 return 0; 99 } 100 }dlx; 101 char s[90],path[90]; 102 struct node 103 { 104 int r, c, v; 105 }nds[MN]; 106 int main() 107 { 108 while (~scanf("%s",s)) 109 { 110 if (s[0] == 'e')break; 111 dlx.init(9*9*9,9*9*4); 112 int r=1; 113 for (int i = 1; i <= 9; i++) 114 { 115 for (int j = 1; j <= 9; j++) 116 { 117 if (s[(i - 1) * 9 + j - 1] == '.') 118 { 119 for (int z = 1; z <= 9; z++) 120 { 121 dlx.link(r, (i - 1) * 9 + j); 122 dlx.link(r, 81 + (i - 1) * 9 + z); 123 dlx.link(r, 162 + (j - 1) * 9 + z); 124 dlx.link(r, 243 + (((i - 1) / 3) * 3 + (j + 2) / 3 - 1) * 9 + z); 125 nds[r].r = i, nds[r].c = j, nds[r].v = z; 126 r++; 127 } 128 } 129 else 130 { 131 int z = s[(i - 1) * 9 + j - 1] - '0'; 132 dlx.link(r, (i - 1) * 9 + j); 133 dlx.link(r, 81 + (i - 1) * 9 + z); 134 dlx.link(r, 162 + (j - 1) * 9 + z); 135 dlx.link(r, 243 + (((i - 1) / 3) * 3 + (j + 2) / 3 - 1) * 9 + z); 136 nds[r].r = i, nds[r].c = j, nds[r].v = z; 137 r++; 138 } 139 } 140 } 141 dlx.ansd = -1; 142 dlx.dance(0); 143 int deep = dlx.ansd; 144 for (int i = 0; i < deep; i++) 145 { 146 int posr = dlx.ans[i]; 147 path[(nds[posr].r - 1) * 9 + nds[posr].c - 1] = '0' + nds[posr].v; 148 } 149 path[deep] = '