ACM练级日志:POJ 3740 与Dancing Links
来源:互联网 发布:点击按钮执行php代码 编辑:程序博客网 时间:2024/05/22 07:41
感觉自己搜索能力还是不够,前些日子记得有个地方出过个题,不会做,一查解题报告发现跟个叫什么Dancing Links的玩意有关,于是就去查了查,然后发现这玩意似乎有点碉……刚刚AC掉模板题,做个记录…
Dancing Links,简称DLX,用于给矩阵精确匹配问题的DFS过程大幅度剪枝,甚至具有自动剪枝的作用。所谓矩阵精确匹配问题,就是你有一个矩阵,里面元素不是0就是1,然后希望你选出一些行,使得这些行的总集中,每一列都有且只有一个1. 原来朴素的DFS就是枚举列,然后把这一列删掉——问题就出在这里,“删掉”一列需要很高的时间复杂度,所以往往我们会只把它标记成“删除了”,但是接下来的搜索中,明明矩阵应该越来越小,我们却花着最初时的代价搜索矩阵,明显很亏。
DLX厉害的地方在于它的数据结构:它用一个十字链表结点来表示每一个矩阵中为1的地方,然后它利用双向链表方便的删除以及删除恢复操作:删除x时只需 R[ L[x] ]= R[x] , L[ R[x] ] = L[x] (我的左边的右边是我的右边,反之亦然), x就找不着了,然后恢复的时候,只需要两句:L[ R[x] ] = x, R[ L[x] ] =x 就又能找着x了。这个对于回溯是非常有用的。
而且DLX解决问题的单一性使它可以作为模板直接使用,前期建模以后,直接构建矩阵然后去DFS就搞定了……所以最麻烦的地方恐怕还是建模。
不管怎样,先放一个刚刚弄的模板吧,只过了POJ 3740, 不知道好不好使。另外momodi的关于DLX的文章非常好懂,代码也很规范,值得一读~
#include<stdio.h>#include<string.h>#include<math.h>using namespace std; const int INF=2<<25;const int MAXNUM=6010; int n,m;int u[MAXNUM], d[MAXNUM], l[MAXNUM], r[MAXNUM];//上下左右int s[MAXNUM], col[MAXNUM];//s[i]:第i列有几个节点,即有几个1,col[i]能告诉你i这个节点在第几列 int head;//总表头,其实就是0号节点int p_nodes;//目前用了多少节点 void del(int c)//删掉列c以及列c中A[i][j]=1的所有行{ l[ r[c] ] = l[c]; r[ l[c] ] = r[c]; for(int i=d[c]; i!=c; i=d[i]) { for(int j=r[i]; j!=i; j=r[j]) { u[ d[j] ] = u[j]; d[ u[j] ] = d[j]; s[ col[j] ] --; } } return;} void resume(int c)//恢复上面的操作{ for(int i=u[c]; i!=c; i=u[i]) { for(int j=l[i]; j!=i; j=l[j]) { s[ col[j] ] ++; d[ u[j] ]=j; u[ d[j] ]=j; } } r[ l[c] ] =c; l[ r[c] ] = c; return;} bool DFS(int depth){ ////TEST //cout<<depth<<endl; if(r[head] == head) return true;//矩阵被删干净了 int min1=INF, now;//挑一个1数最少列的先删 for(int t=r[head]; t!=head; t=r[t]) { if(s[t] <=min1 ) { min1=s[t]; now=t; } } del(now);//删掉此列 int i, j; for(i=d[now]; i!=now; i=d[i]) {//枚举这一列每个1由哪行来贡献,这行即为暂时性的答案,如果需记录答案,此时ans[depth]=i for(j=r[i]; j!=i; j=r[j]) { del( col[j] );//选取了第i行,就要把本行所有的1所在列都删掉 } bool tmp=DFS(depth+1); for(j=l[i]; j!=i; j=l[j]) { resume(col[j]); } if(tmp==true) return true; } resume(now); return false;} void init(){ memset(u,0,sizeof(u)); memset(d,0,sizeof(d)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); memset(s,0,sizeof(s)); memset(col,0,sizeof(col)); head=0; p_nodes=0;} void InitLinks()//注意:该链表两个方向都是循环的,最上面的上面指最下面{ int i,j; r[head]=1; l[head]=m; for(i=1;i<=m;i++)//制作列的表头,使用结点1~m { l[i]=i-1; r[i]=i+1; if(i==m) r[i]=head; u[i]=i; d[i]=i; s[i]=0; col[i]=i; } p_nodes=m; for(i=1;i<=n;i++) { int row_first1=-1;//看看这一行是不是已经有第一个1了 for(j=1;j<=m;j++) { int k; scanf("%d", &k); if(k==1) { p_nodes++; s[j] ++; u[p_nodes] = u[j]; d[ u[j] ] = p_nodes; u[j] = p_nodes; d[p_nodes] = j; col[p_nodes] = j;//和列的关系处理完毕 if(row_first1==-1) { l[p_nodes] = r[p_nodes] = p_nodes; row_first1=p_nodes; } else { l[p_nodes] = l[row_first1]; r[ l[row_first1] ] = p_nodes; r[p_nodes] = row_first1; l[ row_first1 ]=p_nodes;//和行的关系处理完毕 } } } }} int main(){ while(scanf("%d %d", &n, &m) ==2) { init(); InitLinks(); bool ok=true; for(int i=1;i<=m;i++) { if(s[i]==0) { ok=false; break; } } if(ok) ok=DFS(1); if(ok) printf("Yes, I found it\n"); else printf("It is impossible\n"); } return 0;}
0 0
- ACM练级日志:POJ 3740 与Dancing Links
- poj 3740 dancing links
- POJ 3740 Dancing Links
- poj 3740 dancing links
- ACM练级日志: POJ 1389
- ACM练级日志: POJ 1376
- 舞动的dancing links poj 3740
- poj 3740 Easy Finding(Dancing Links)
- POJ 3740 - Easy Finding (Dancing links)
- ACM练级日志:POJ 3074 数独与DLX
- ACM练级日志: POJ 2155、1151
- poj 3076 Sudoku //Dancing Links
- POJ 3076 Sudoku (dancing links)
- poj 3074 Sudoku(Dancing Links)
- poj 3740 Easy Finding//Dancing Links 或 状态压缩Dp
- poj 3740 Easy Finding(Dancing Links 精确覆盖)
- Dancing links ? Dancing links !
- ACM练级日志:set
- 在CentOS 6.4中编译安装gcc 4.8.1 + gdb 7.6.1 + Eclipse
- 还是畅通工程
- 关于Umeng错误信息调试
- jsp - 下拉列表框
- spring与jersey的集成
- ACM练级日志:POJ 3740 与Dancing Links
- vector和list区别
- HDU 1862 EXCEL排序 (排序水题)
- 继续畅通工程
- Android学习Scroller(四)——实现拉动后回弹的布局
- 继承、方法重写和多态
- ACM练级日志:POJ 3074 数独与DLX
- servlet容器中使用jersey 翻译官网
- 接口框架等知识点小结