没学过舞蹈链的可看这篇博客:跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题(大佬讲的是真好呀)
此篇博客仅提供一段封装好的代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 5500;
struct DLX {
int ans[maxn]; //储存答案
int n, m, x;
int cnt, lin[maxn], sz[maxn]; //链中的第cnt个元素;第i行的第一个数为lin[i];第[j]列的元素个数为sz[j]
int l[maxn], r[maxn], u[maxn], d[maxn];//第i元素左、右、上、下的元素的下标
int col[maxn], row[maxn]; //第i个元素在第col[i]行,row[i]列
inline void build() { //建造矩阵
for (register int i = 0; i <= m; i++) //用双向链表储存列表头
l[i] = i - 1, r[i] = i + 1, u[i] = d[i] = i;
l[0] = m, r[m] = 0, cnt = m; //构成双向循环链表,矩阵中已存cnt个元素
for(register int i = 1; i <= n; i += 1) {
for(register int j = 1; j <= m; j += 1)
{
scanf("%d", &x);
if(x) insert(i, j); //将这个在i行 j列的元素 加入矩阵、链表中
}
}
}
inline void insert(int ro, int co) { //向矩阵中加入新的元素
cnt++;
row[cnt] = ro; //第cnt个元素所在行
col[cnt] = co; //第cnt个元素所在列
sz[co]++; //第co列的元素个数 +1
/* 处理纵向的链表: */ //头插法!!!!!!!!!!!!!!!!!!!!!!!!
u[cnt] = co; // 将第cnt个元素 加入 纵向的 链表中
d[cnt] = d[co]; // 同上
u[d[co]] = cnt; // 同上
d[co] = cnt; // 同上
/* 处理横向的链表: */
if(lin[ro]==0) { //如果这一行链表中没有元素 所执行的操作
lin[ro] = cnt; //将第cnt个元素 加入 横向的 链表中 这部分操作相当于添加表头(不像纵向已经有表头)!!!!!!
l[cnt] = cnt; // 同上
r[cnt] = cnt; // 同上
}
else { //反之,执行的操作
l[cnt] = lin[ro]; //将第cnt个元素 加入 横向的 链表中
r[cnt] = r[lin[ro]]; // 同上
l[r[lin[ro]]] = cnt; // 同上
r[lin[ro]] = cnt; // 同上
}
}
inline void cut(int c) { //将第c列移除
l[r[c]] = l[c]; //将第c列从表头中删除(将该表头元素的 左右表头元素 相连)
r[l[c]] = r[c];
for (register int i = d[c]; i != c; i = d[i]) //从 纵向链表 中移除 第c列中的每个元素所在一行
for (register int j = r[i]; j != i; j = r[j])//从 纵向链表 中移除 该行元素
{
u[d[j]] = u[j]; //将 该行的每一个元素从 纵向链表 中移除(将此元素的上下两元素相连)
d[u[j]] = d[j];
sz[col[j]]--; //第 该元素 所在列的 列元素个数 -1
}
}
inline void back(int c) { //将第c列恢复
for (register int i = d[c]; i != c; i = d[i]) //恢复 纵向链表 中 第c列中的被移除行
for (register int j = r[i]; j != i; j = r[j]) {//恢复 纵向链表 中 被移出行中被移除的元素
u[d[j]] = d[u[j]] = j; //将 该行的每一个元素从 纵向链表 中恢复(将此元素接入 上下两元素中间)
sz[col[j]]++; //第 该元素 所在列的 列元素个数 +1
}
l[r[c]] = r[l[c]] = c; //将第c列从表头中恢复
}
inline void print(int ln) { //输出答案
for(register int i = 1; i < ln; i++)
printf("%d ", ans[i]);
}
inline bool dance(int deep) { /*deep 表示递归深度(即删除列的个数)*/ //Start Dancing!
if(r[0]==0) { //空矩阵则说明完成了 精确覆盖 ,输出答案
print(deep);
return true;
}
int c = r[0];
for (register int i = c; i != 0; i = r[i]) //此循环用来找矩阵中元素最少的一列,以减少决策树,节省时间
if (sz[c] > sz[i])
c = i;
cut(c); //移除第c列
for (register int i = d[c]; i != c; i = d[i]) { //依次列举第c列中有元素的每一行
ans[deep] = row[i]; //记录路径,为下午 输出答案 做铺垫,埋伏笔
for (register int j = r[i]; j != i; j = r[j])//将选中第 col[j]行,删除相关元素
cut(col[j]);
if (dance(deep + 1))
return true;//如果为真则说明 上文中 已完成精确覆盖任务,回溯
for (register int j = r[i]; j != i; j = r[j]) //如果为假则说明 该列并不在答案中,下次循环选取下一列
back(col[j]);//将选中第 col[j]行,恢复相关元素
}
back(c); //恢复第c列
return 0; //表明 上文为达到题目要求,返回值为 假,回溯
}
};
DLX dlx;
int main() {
scanf("%d%d", &dlx.n, &dlx.m);
dlx.build();
if(!dlx.dance(1))
printf("No Solution!\n");
return 0;
}