poj3074 poj3076 数独问题 dancing links

当初学习dancing links的最大目的就是为了解决数独的问题吧,但是经过这两天的学习之后发现dancing links的功能远不止于此,看来是得多挖掘一下。

当然本文还是介绍解数独的做法。

首先,需要把数度问题转化为一个最小覆盖问题:

1、每个空格最终都会填上一个数,而且有1,2,3,4,5,6,7,8,9这九种可能(介绍3*3的,其他大小的同理),我们可以把每个空格中填写的数字情况看成是dancing links中的行,那么最多就会有9*9*9个行了

2、列代表什么含义呢?我们填写数字是为了达到这样的目的:每行包含1-9,每列包含1-9,9个3*3小块包含1-9,以及81个格子填满,所以,可以这么设置列:

前面81列表示对应的格子里是否填数,然后9*9=81列表示,第几行里是否已经填了几,例子:第5行里已经有4了,那么就是在这81列中的第9*(5-1)+4列;再往后还是81列,只不过对应第几列里是否有几;最后81列则是表示第几个3*3小块里是否有几

然后,从1中所谓的行中选出81行(因为每行代表一个格子),使得这81行恰好覆盖了这里的81*4列


问题转化完成。

然后就是对数独的处理,在读入数独的时候,如果某个格子上已经有数字填了,那么该格子相当于就是对应一行,直接插入;如果没填数,那么要假设这个格子填1-9,所以得加入九行。

更新列时:每一个格子确定一个数,那么一定会在4个列中增加元素(81*4),每个81列中都会出现一个(原因见上面的分析)

等dancing links建立完毕之后,就是一个exact cover问题了,借用之前的代码即可

以下是我解决poj3076的代码(16*16的数独)

我这里只要改变LEN这个常量值就可以计算不同大小的数独,如16*16就是4,9*9就是3,N*N就是根号N

#include <cstdio>
#include <cstring>
const int LEN = 4;
const int SIZE =LEN*LEN;
const int M = SIZE*SIZE*4+10;
const int N = SIZE*SIZE*SIZE*4+M+10;

int R[N],L[N],U[N],D[N],C[N],Row[N];
int S[M],O[M];
int n,m;
int ans; // 需要拿掉几行
int size; //节点个数

struct node
{
	int i,j,x;
}res[N];

void init()
{
	memset(S,0,sizeof(S)); //初始的时候每列上1的个数为0

	//链表初始化
	for(int i=1; i<=m; i++)
	L[i+1] = R[i-1] = U[i] = D[i] = i;
	R[m] = 0; //0表示表头
	size = m + 1; //新的节点的下标是从m+1开始的,当然也可以随机的设置一个更大的值,没有影响
}

//删除第c列,以及该列中元素对应的所有行
void remove(int c)
{
	R[L[c]] = R[c];
	L[R[c]] = L[c];
	for(int i=D[c]; i!=c; 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]]--;
	}
}

//恢复第c列,以及该列中元素对应的所有行
void resume(int c)
{
	for(int i=U[c]; i!=c; i=U[i])
	for(int j=L[i]; j!=i; j=L[j])
	{
		U[D[j]] = D[U[j]] = j;
		S[C[j]]++;
	}
	R[L[c]] = L[R[c]] = c;
}

bool dfs(int k) //k表示当前已经拿掉的行数
{
	if(R[0]==0) //表示所有的列已经被拿完
	{
		ans = k;
		return 1;
	}
	int min = M,c=-1;
	//选取元素个数最少的列
	for(int i=R[0]; i!=0; i=R[i])
	if(S[i]<min) min = S[i],c = i;
	remove(c);
	for(int i=D[c]; i!=c; i=D[i])
	{
		for(int j=R[i]; j!=i; j=R[j])
		remove(C[j]);
		O[k] = Row[i]; //当前拿掉的是i那个元素对应的行
		if(dfs(k+1)) return 1;
		for(int j=L[i]; j!=i; j=L[j])
		resume(C[j]);
	}
	resume(c);
	return 0;
}

void insertCol(int col)
{
	C[size] = col;
	U[size] = U[col];
	D[U[col]] = size;
	U[col] = size;
	D[size] = col;
	S[col]++;
}

void insertRow(int &rowhead)
{
	if(rowhead==-1)
	{
		L[size] = R[size] = size;
		rowhead = size;
	}
	else
	{
		L[size] = L[rowhead];
		R[L[rowhead]] = size;
		L[rowhead] = size;
		R[size] = rowhead;
	}
}

void addNode(int i,int j,int y)
{
	int rowhead = -1;
	insertCol(SIZE*(i-1)+j);
	insertRow(rowhead);
	Row[size] = n;
	size++;
	insertCol(SIZE*SIZE+(i-1)*SIZE+y);
	insertRow(rowhead);
	Row[size] = n;
	size++;
	insertCol(SIZE*SIZE*2+(j-1)*SIZE+y);
	insertRow(rowhead);
	Row[size] = n;
	size++;
	insertCol(SIZE*SIZE*3+(((i+LEN-1)/LEN-1)*LEN+(j+LEN-1)/LEN-1)*SIZE+y);
	insertRow(rowhead);
	Row[size] = n;
	size++;
	res[n].i = i;
	res[n].j = j;
	res[n].x = y;
	n++;
}

int main()
{
	//freopen("in","r",stdin);
	char s[300];
	m = SIZE*SIZE*4;
	int cnt = 0;
	while(cnt==0||gets(s+1))
	{
		cnt++;
		for(int i=0; i<SIZE; i++)
		gets(s+i*SIZE+1);
		init();
		n = 1;
		for(int i=1; i<=SIZE; i++)
		for(int j=1; j<=SIZE; j++)
		{
			char x = s[SIZE*(i-1)+j];
			if(x!='-')
			{
				int y = x - 'A' + 1;
				addNode(i,j,y);
			}
			else
			{
				for(int y=1; y<=SIZE; y++) addNode(i,j,y);
			}
		}
		dfs(0);
		int tmp[SIZE+1][SIZE+1];
		for(int i=0; i<ans; i++)
		{
			int l = O[i];
			tmp[res[l].i][res[l].j] = res[l].x;
		}
		if(cnt>1) puts("");
		for(int i=1; i<=SIZE; i++,puts(""))
		for(int j=1; j<=SIZE; j++)
		printf("%c", tmp[i][j]+'A'-1);
		
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值