P4929 【模板】舞蹈链(DLX)

没学过舞蹈链的可看这篇博客:跳跃的舞者,舞蹈链(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值