跳至内容
CVBB ACM Team
用户工具
注册
登录
站点工具
搜索
工具
显示页面
修订记录
Copy this page
导出 PDF
反向链接
最近更改
媒体管理器
网站地图
注册
登录
>
最近更改
媒体管理器
网站地图
您在这里:
front_page
»
2020-2021
»
teams
»
no_morning_training
»
shaco
»
知识点
»
搜索
»
dlx
2020-2021:teams:no_morning_training:shaco:知识点:搜索:dlx
本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。
====== DLX(Dancing Links X 舞蹈链) ====== ===== 简介 ===== 求解精准覆盖问题:给定一个集合和若干个子集,选择若干个子集使得原集合中每一个元素出现且仅出现一次。 ===== 思路 ===== 用矩阵的方式存储子集:每一行代表一个子集,每一列代表原集合中的一个元素,某行某列用0和1代表这一个子集是否含有这一个元素。 1. 对于一个存在1的列找一个行\\ 2. 删除该行、行上点的所在列、包含行上点所在列的行$\to$1.\\ 3. 余下列但无可用行$\to$回溯\\ 4. 不余——输出答案/储存答案\\ ==== 实现方法 ==== 利用十字循环链表。 === 初始化 === <hidden code> <code cpp> void init() { cnt=m; for(int i=0;i<=m;i++) node[i]={i,i,i-1,i+1,0,i}; node[m].d=0; node[0].a=m; } </code> </hidden> === 添加点 === <hidden code> <code cpp> for(int i=1;i<=n;i++) for(int j=1,a;j<=m;j++) { scanf("%d",&a); if(a) { tot[j]++; node[++cnt]={node[j].w,j,0,0,i,j}; node[node[j].w].s=cnt; node[j].w=cnt; if(!left[i]) left[i]=node[cnt].a=node[cnt].d=cnt; else { node[cnt].d=left[i]; node[cnt].a=node[left[i]].a; node[node[left[i]].a].d=cnt; node[left[i]].a=cnt; } } } </code> </hidden> === 删除列 === <hidden code> <code cpp> void remove(int x) { node[node[x].a].d=node[x].d; node[node[x].d].a=node[x].a; for(int i=node[x].s;i!=x;i=node[i].s) for(int j=node[i].d;j!=i;j=node[j].d) { node[node[j].w].s=node[j].s; node[node[j].s].w=node[j].w; tot[node[j].c]--; } } </code> </hidden> === 恢复列 === <hidden code> <code cpp> void recover(int x) { for(int i=node[x].w;i!=x;i=node[i].w) for(int j=node[i].d;j!=i;j=node[j].d) { node[node[j].w].s=node[node[j].s].w=j; tot[node[j].c]++; } node[node[x].a].d=node[node[x].d].a=x; } </code> </hidden> === X主体 === <hidden code> <code cpp> bool dlx(int dep) { if(!node[0].d) { for(int i=1;i<dep;i++) printf("%d ",ans[i]); return true; } int C=node[0].d; for(int i=node[C].d;i;i=node[i].d) if(tot[i]<tot[C]) C=i; if(node[C].s==C) return false; remove(C); for(int i=node[C].s;i!=C;i=node[i].s) { ans[dep]=node[i].r; for(int j=node[i].a;j!=i;j=node[j].a) remove(node[j].c); if(dlx(dep+1)) return true; for(int j=node[i].d;j!=i;j=node[j].d) recover(node[j].c); } recover(C); return false; } </code> </hidden> 注:恢复列时要从删除列的最后一个开始恢复。 ===== 例题 ===== ==== 【模板】舞蹈链(DLX) ==== **来源**:洛谷p4929 **概述**:模板 **答案**:模板 <hidden code> <code cpp> #include<cstdio> using namespace std; int n,m,cnt,left[505],ans[505],tot[505]; struct unit{int w,s,a,d,r,c;}node[6000]; void init() { cnt=m; for(int i=0;i<=m;i++) node[i]={i,i,i-1,i+1,0,i}; node[m].d=0; node[0].a=m; } void remove(int x) { node[node[x].a].d=node[x].d; node[node[x].d].a=node[x].a; for(int i=node[x].s;i!=x;i=node[i].s) for(int j=node[i].d;j!=i;j=node[j].d) { node[node[j].w].s=node[j].s; node[node[j].s].w=node[j].w; tot[node[j].c]--; } } void recover(int x) { for(int i=node[x].w;i!=x;i=node[i].w) for(int j=node[i].d;j!=i;j=node[j].d) { node[node[j].w].s=node[node[j].s].w=j; tot[node[j].c]++; } node[node[x].a].d=node[node[x].d].a=x; } bool dlx(int dep) { if(!node[0].d) { for(int i=1;i<dep;i++) printf("%d ",ans[i]); return true; } int C=node[0].d; for(int i=node[C].d;i;i=node[i].d) if(tot[i]<tot[C]) C=i; if(node[C].s==C) return false; remove(C); for(int i=node[C].s;i!=C;i=node[i].s) { ans[dep]=node[i].r; for(int j=node[i].a;j!=i;j=node[j].a) remove(node[j].c); if(dlx(dep+1)) return true; for(int j=node[i].d;j!=i;j=node[j].d) recover(node[j].c); } recover(C); return false; } int main() { freopen("input.in","r",stdin); //freopen("output.out","w",stdout); scanf("%d%d",&n,&m); init(); for(int i=1;i<=n;i++) for(int j=1,a;j<=m;j++) { scanf("%d",&a); if(a) { tot[j]++; node[++cnt]={node[j].w,j,0,0,i,j}; node[node[j].w].s=cnt; node[j].w=cnt; if(!left[i]) left[i]=node[cnt].a=node[cnt].d=cnt; else { node[cnt].d=left[i]; node[cnt].a=node[left[i]].a; node[node[left[i]].a].d=cnt; node[left[i]].a=cnt; } } } if(!dlx(1)) printf("No Solution!"); return 0; } </code> </hidden> ---- ===== 总结 =====
2020-2021/teams/no_morning_training/shaco/知识点/搜索/dlx.txt
· 最后更改: 2020/08/20 20:04 由
shaco
页面工具
显示页面
修订记录
反向链接
Copy this page
导出 PDF
回到顶部