对跳舞链(Dancing Links)和精确覆盖(DLX)的理解运用

1 篇文章 0 订阅
1 篇文章 0 订阅

精确覆盖问题

n n n m m m 01 01 01 矩形,从中选择若干行使得每一列有且恰好只有一个 1 1 1
hihocoder-1317
主要注意双向十字循环链表写法, r e m o v e remove remove r e s o t r e resotre resotre互逆,DFS写法
这里的链表插入方法只适合跳舞链
remove时只改变上下信息,左右信息不改变
r e m o v e ( c ) remove(c) remove(c):删除c列的所有行
DFS流程细节理解
第一次 r e m o v e remove remove时已将所有可选行删除,之后调用 r e m o v e ( c ) remove(c) remove(c)对所有可选行无影响

#include<set>
#include<map>
#include<cmath>
#include<deque>
#include<stack>
#include<ctime>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
#define LL long long
#define ULL unsigned long long
using namespace std;
int read(){
	int f=1,x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return f*x;
}
#define MAXN 1000
#define INF 0x3f3f3f3f
struct Node{
	int L,R,U,D,row,col;
}node[MAXN*MAXN+5];
int n,m,ncnt,Head,Cnt[MAXN+5];
void Init(){
	ncnt=-1,Head=0,memset(Cnt,0,sizeof(Cnt));
	for(int i=0;i<=m;i++)
		++ncnt,node[i].L=i-1,node[i].R=i+1,node[i].U=i,node[i].D=i;
	node[0].L=m,node[m].R=0;
	return ;
}
void Addnode(int i,int j,int st,int ed){
	++ncnt,node[ncnt]=(Node){ed,st,node[j].U,j,i,j},Cnt[j]++;
	node[st].L=node[ed].R=ncnt;
	node[node[j].U].D=ncnt,node[j].U=ncnt;
	return ;
}
void Remove(int c){//删除第c列有节点的所有行的上下信息
	node[node[c].L].R=node[c].R;
	node[node[c].R].L=node[c].L;
	for(int i=node[c].D;i!=c;i=node[i].D)
		for(int j=node[i].R;j!=i;j=node[j].R){
			node[node[j].U].D=node[j].D;
			node[node[j].D].U=node[j].U;
			Cnt[node[j].col]--;
		}
	return ;
}
void Restore(int c){
	node[node[c].L].R=c;
	node[node[c].R].L=c;
	for(int i=node[c].U;i!=c;i=node[i].U)
		for(int j=node[i].L;j!=i;j=node[j].L){
			node[node[j].U].D=j;
			node[node[j].D].U=j;
			Cnt[node[j].col]++;
		}
	return ;
}
int order[MAXN+5];
bool DFS(int d){
	if(node[Head].R==Head)
		return 1;
	int Min=INF,c;
	for(int j=node[Head].R;j!=Head;j=node[j].R)
		if(Cnt[j]<Min)//可行解剪枝
			Min=Cnt[j],c=j;
	Remove(c);
	for(int i=node[c].D;i!=c;i=node[i].D){
		order[d]=node[i].row;
		for(int j=node[i].R;j!=i;j=node[j].R)
			Remove(node[j].col);
		if(DFS(d+1))
			return 1;
		for(int j=node[i].L;j!=i;j=node[j].L)
			Restore(node[j].col);
	}
	Restore(c);
	return 0;
}
int main(){
	int T=read();
	while(T--){
		n=read(),m=read();
		Init();
		for(int i=1;i<=n;i++){
			int st=ncnt+1,ed=ncnt+1;
			for(int j=1;j<=m;j++){
				int opt=read();
				if(!opt) continue;
				Addnode(i,j,st,ed),ed=ncnt;
				
			}
		}
		for(int j=1;j<=m;j++)
			if(Cnt[j]==0){
				puts("No");
				goto Break;
			}
		puts(DFS(1)==true?"Yes":"No");
		Break:;
	}
	return 0;
}

问题运用

可以抽象为选择第 i i i 行是 选择某元素或某决策,列是对选择的限制或理解为任务 即:
j j j 列的 1 1 1 :对于任务 j j j 通过选择行只有一行满足
所以该算法运用于各种填数游戏中

#include<set>
#include<map>
#include<cmath>
#include<deque>
#include<stack>
#include<ctime>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
#define LL long long
#define ULL unsigned long long
using namespace std;
int read(){
	int f=1,x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return f*x;
}
#define MAXN 1000
#define INF 0x3f3f3f3f
struct Node{
	int L,R,U,D,row,col;
}node[MAXN+5];
int ncnt,Head,Row,Cnt[MAXN+5];
void Init(int M){
	ncnt=-1,Head=0,memset(Cnt,0,sizeof(Cnt));
	for(int i=0;i<=M;i++)
		++ncnt,node[i].L=i-1,node[i].R=i+1,node[i].U=i,node[i].D=i;
	node[0].L=M,node[M].R=0;
	return ;
}
void Addnode(int i,int j,int st,int ed){
	++ncnt,node[ncnt]=(Node){ed,st,node[j].U,j,i,j},Cnt[j]++;
	node[st].L=node[ed].R=ncnt;
	node[node[j].U].D=ncnt,node[j].U=ncnt;
	return ;
}
void Remove(int c){//删除第c列所有节点的左右信息
	for(int i=node[c].D;i!=c;i=node[i].D)
		node[node[i].L].R=node[i].R,node[node[i].R].L=node[i].L;
	return ;
}
void Restore(int c){
	for(int i=node[c].U;i!=c;i=node[i].U)
		node[node[i].L].R=i,node[node[i].R].L=i;
	return ;
}
bool visc[MAXN+5];
int ansd,order[MAXN+5];
int h(){//至少需要步数
	int ret=0;
	for(int c=node[Head].R;c!=Head;c=node[c].R)
		visc[c]=1;
	for(int c=node[Head].R;c!=Head;c=node[c].R)
		if(visc[c]){
			ret++;
			visc[c]=0;
			for(int i=node[c].D;i!=c;i=node[i].D)
				for(int j=node[i].R;j!=i;j=node[j].R)
					visc[node[j].col]=0;
			//printf("%d ",node[j].col);
		}
	return ret;
}
void DFS(int d){
	if(d+h()>=ansd)
		return ;
	if(node[Head].R==Head){
		ansd=d;
		return ;
	}
	int c=node[Head].R;
	for(int j=node[Head].R;j!=Head;j=node[j].R)
		if(Cnt[j]<Cnt[c])
			c=j;
	for(int i=node[c].D;i!=c;i=node[i].D){
		order[d]=node[i].row;
		Remove(i);
		for(int j=node[i].R;j!=i;j=node[j].R)
			Remove(j);
		DFS(d+1);
		for(int j=node[i].L;j!=i;j=node[j].L)
			Restore(j);
		Restore(i);
	}
	return ;
}
int n,m,a[20][20];
int Id(int x,int y){return (x-1)*m+y;}
int main(){
	while(~scanf("%d %d",&n,&m)){
		int cnt=0;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++){
				a[i][j]=read();
				if(a[i][j])
					a[i][j]=++cnt;
			}
		Init(cnt);
		int n1=read(),m1=read();
		ansd=INF,Row=0;
		for(int i=1;i<=n-n1+1;i++)
			for(int j=1;j<=m-m1+1;j++){
				Row++;
				int st=ncnt+1,ed=ncnt+1;
				for(int x=i;x<=i+n1-1;x++)
					for(int y=j;y<=j+m1-1;y++)
						if(a[x][y])
							Addnode(Row,a[x][y],st,ed),ed=ncnt;
			}
		DFS(0);
		printf("%d\n",ansd);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值