本文主要是介绍HUST 1017 Exact cover(舞蹈链不能为了ac而ac),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
题目链接:[kuangbin带你飞]专题三 Dancing Links A - Exact cover
题意
给定一01矩阵,问是否能够精确覆盖(就是选取任意行,这些行的1所在的列互不冲突且完整覆盖所有列),若有输出行号(要按递增顺序输出),否则输出NO。
思路
ps:两个礼拜前大略看了下舞蹈链(虽然英文名听起来更高端,但还是更喜欢它的中文名字),很精妙但也让人一看就惰性必生不愿再看,今天耐心再仔细理解了下,总算是a的刷题生涯第一道精确覆盖题(人有时候还是要逼自己一把,战胜惰性才能进步)。
没什么思路,标准的舞蹈链模版题,理解了舞蹈链就能a了。
学习舞蹈链,推荐此博文,相当清晰:跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题
代码上有详细的注释,但有一处有必要分享一下。
for(int i=R[0]; i!=0; i=R[i])if(S[i] < S[col])col = i;
上面这段代码作用是每次选取元素最少的列进行操作,可以有效减少递归层数,从而加快程序效率。
因为本人好奇,取消了它试了一下,发现wrong(按理也应该是timelimit啊),一试再试,发现是得出的行号没有排序的原因,加上对结果的排序就好了。
但本人又好奇了,一再思索,终究还是想不出来上述代码为什么能够使结果递增序,于是做了一组数据:
5 5
2 4 5
3 1 3 4
1 5
1 2
2 1 5
测试后发现无论加不加上述代码结果都不是递增序的,也就是说上述代码只是加速的功能而已。
也就是说,测试数据太水,所有没加排序的代码都应该被wrong。(很好奇那么多题解为什么没有一个带排序的,希望自己不要成为为了ac而ac的人)。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>using namespace std;const int N = 1009;
const int MAX = 1000009;int U[MAX], D[MAX], L[MAX], R[MAX];//数组模拟链表指向(上下左右)
int C[MAX], M[MAX];//节点所在列与行
int S[N];//储存每列的元素数量
int H[N];//行头指针
int ANS[N];//结果保存数组void link(int row, int col, int id)//将节点加入链表
{C[id] = col; M[id] = row;//记录行列U[id] = U[col]; D[U[col]] = id;//上下连接D[id] = col; U[col] = id;if(H[row] == -1)//左右连接(使用表头方便头插)H[row] = L[id] = R[id] = id;else{L[id] = L[H[row]]; R[L[H[row]]] = id;L[H[row]] = id; R[id] = H[row];}S[col]++;
}void remove(int col)//删除列
{R[L[col]] = R[col];L[R[col]] = L[col];for(int i=D[col]; i!=col; i=D[i]){for(int j=R[i]; j!=i; j=R[j]){U[D[j]] = U[j];D[U[j]] = D[j];S[C[j]]--;}}
}void resume(int col)//恢复列(先删的后恢复,后删的先恢复,所以跟remove反向操作)
{R[L[col]] = col;L[R[col]] = col;for(int i=U[col]; i!=col; i=U[i]){for(int j=L[i]; j!=i; j=L[j]){U[D[j]] = j;D[U[j]] = j;S[C[j]]++;}}
}bool dance(int k)
{if(!R[0])//列辅助数组为空表示已得解{printf("%d", k);sort(ANS, ANS+k);//对结果排序。for(int i=0; i<k; i++)printf(" %d", ANS[i]);printf("\n");return true;}int col = R[0];for(int i=R[0]; i!=0; i=R[i])//加速,上文已说明if(S[i] < S[col])col = i;remove(col);//删除列for(int i=D[col]; i!=col; i=D[i])//尝试该列每行一次做为解{ANS[k] = M[i];// 记录行号for(int j=R[i]; j!=i; j=R[j])//删除该行元素说相关的列remove(C[j]);if(dance(k+1))return true;for(int j=L[i]; j!=i; j=L[j])//恢复resume(C[j]);}resume(col);//恢复列return false;
}int main()
{int n, m;while(~scanf("%d%d", &n, &m)){for(int i=0; i<=m; i++)//初始化{L[i+1] = i;R[i] = i+1;U[i] = D[i] = i;S[i] = 0;}L[0] = m;R[m] = 0;int id = m+1;for(int i=1; i<=n; i++){int num;scanf("%d", &num);H[i] = -1;while(num--){int col;scanf("%d", &col);link(i, col, id++);}}if(!dance(0))printf("NO\n");}return 0;
}
这篇关于HUST 1017 Exact cover(舞蹈链不能为了ac而ac)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!