舞蹈链

解决精确覆盖问题

精确覆盖问题:在一个全集X中若干子集的集合为S,精确覆盖Exactcover是指,S的子集S,满足X中的每一个元素在S中恰好出现一次。在计算机科学中,精确覆盖问题指找出这样的一种覆盖,或证明其不存在。

对于重复覆盖问题,感觉跟深搜差不多,甚至玄学剪枝后深搜会更快,所以不去探索舞蹈链写法了。

P4929 【模板】舞蹈链(DLX)

代码,详细解释看注释:

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define inf 1e18
#define inc 0xcfcfcfcf
#define N 507
#define M 500007
#define mod 1000000007
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
using namespace std;
int T=1,n,m,head,lcnt;
int l[N*N],r[N*N],u[N*N],d[N*N],row[N*N],col[N*N],num[N];
vector<int> g[N];
void Init()//生成每列头结点 
{
	for(int i=0;i<=m;++i)
	{
		u[i]=d[i]=i;
		l[i]=i-1;
		r[i]=i+1;
	}
	l[head]=m;//辅助结点左右相连 
	r[m]=head;
	lcnt=m+1;
}
void Addrow(int x)//加入第x行 
{
	int first=lcnt;//记录行初始的那个点 
	for(int i=0;i<g[x].size();++i)
	{
		int j=g[x][i];
		u[lcnt]=u[j];
		d[u[j]]=lcnt;
		u[j]=lcnt;
		d[lcnt]=j;
		l[lcnt]=lcnt-1;//先直接连接,后面再处理左右两头 
		r[lcnt]=lcnt+1;
		col[lcnt]=j;
		row[lcnt]=x;
		++num[j];
		++lcnt;
	}
	l[first]=lcnt-1;//左右两头修改 
	r[lcnt-1]=first;
}
void Remove(int x)//删除第x列并且将该列上有1的行与上下行脱离 
{
	r[l[x]]=r[x];
	l[r[x]]=l[x];
	for(int i=d[x];i!=x;i=d[i])
		for(int j=r[i];j!=i;j=r[j])//因为j!=i,所以该列上下仍然相连,因此可以恢复 
		{
			u[d[j]]=u[j];
			d[u[j]]=d[j];
			--num[col[j]];
		}
}
void Resume(int x) //恢复同理 
{
	for(int i=u[x];i!=x;i=u[i])
		for(int j=l[i];j!=i;j=l[j])
		{
			d[u[j]]=j;
			u[d[j]]=j;
			++num[col[j]];
		}
	r[l[x]]=x;
	l[r[x]]=x; 
}
bool Dance(int dep)
{
	if(r[head]==head)
		return 1;
	int now=r[head];
	for(int i=now;i!=head;i=r[i])//剪枝,找点数少的列准备删除 
		if(num[i]<num[now])
			now=i;
	Remove(now); 
	for(int i=d[now];i!=now;i=d[i])
	{
		for(int j=r[i];j!=i;j=r[j])//删除该列上有1的行所在的所有列 
			Remove(col[j]);
		if(Dance(dep+1)) 
		{
			printf("%lld ",row[i]);
			return 1;
		}
		for(int j=l[i];j!=i;j=l[j])
			Resume(col[j]);
	}
	Resume(now);
	return 0;
}
bool Solve()
{
 	//freopen("test.in","r",stdin);
	scanf("%lld%lld",&n,&m);
	Init();
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			int in;
			scanf("%lld",&in);
			if(in)
				g[i].push_back(j);
		}
		Addrow(i);
	}
	if(!Dance(1))
		printf("No solution!");
	printf("\n"); 
	return true;
}
signed main()
{
	//scanf("%lld",&T);
	while(T--)
		if(!Solve())
			printf("-1\n");
	return 0;
}
/*
-std=c++11
-std=c99
*/


posted @   模拟退火  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示