HDU 4069 Squiggly Sudoku 数独DLX 福州网络赛

 

Squiggly Sudoku

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 644 Accepted Submission(s): 111


Problem Description
Today we play a squiggly sudoku, The objective is to fill a 9*9 grid with digits so that each column, each row, and each of the nine Connecting-sub-grids that compose the grid contains all of the digits from 1 to 9.
Left figure is the puzzle and right figure is one solution.

Now, give you the information of the puzzle, please tell me is there no solution or multiple solution or one solution.

Input
The first line is a number T(1<=T<=2500), represents the number of case. The next T blocks follow each indicates a case.
Each case contains nine lines, Each line contains nine integers.
Each module number tells the information of the gird and is the sum of up to five integers:
0~9: '0' means this gird is empty, '1' - '9' means the gird is already filled in.
16: wall to the up
32: wall to the right
64: wall to the down
128: wall to the left
I promise there must be nine Connecting-sub-grids, and each contains nine girds.

Output
For each case, if there are Multiple Solutions or no solution just output "Multiple Solutions" or "No solution". Else output the exclusive solution.(as shown in the sample output)

Sample Input

3
144 18 112 208 80 25 54 144 48
135 38 147 80 121 128 97 130 32
137 32 160 144 114 167 208 0 32
192 100 160 160 208 96 183 192 101
209 80 39 192 86 48 136 80 114
152 48 226 144 112 160 160 149 48
128 0 112 166 215 96 160 128 41
128 39 153 32 209 80 101 136 35
192 96 200 67 80 112 208 68 96

144 48 144 81 81 16 53 144 48
128 96 224 144 48 128 103 128 38
163 208 80 0 37 224 209 0 32
135 48 176 192 64 112 176 192 104
192 101 128 89 80 82 32 150 48
149 48 224 208 16 48 224 192 33
128 0 114 176 135 0 80 112 169
137 32 148 32 192 96 176 144 32
192 96 193 64 80 80 96 192 96

144 88 48 217 16 16 80 112 176
224 176 129 48 128 40 208 16 37
145 32 128 96 196 96 176 136 32
192 32 227 176 144 80 96 192 32
176 192 80 98 160 145 80 48 224
128 48 144 80 96 224 183 128 48
128 36 224 144 51 144 32 128 105
131 64 112 136 32 192 36 224 176
224 208 80 64 64 116 192 83 96


Sample Output
Case 1:
521439678
763895124
984527361
346182795
157964832
812743956
235678419
479216583
698351247
Case 2:
No solution
Case 3:
Multiple Solutions
 
 
DLX模板题,原来不会判断多解。。。
dfs的时候如果成功搜索两次,说明存在多解,
成功搜索一次则存在唯一解,
否则无解。
用一个DFS预处理每一个格子所属块。
---------------------------------------------------------------------------------------------

Dancing Links是用来优化一类精确覆盖问题中的DFS过程。

精确覆盖问题是指在一个01矩阵中,选出一些行使每一列有且仅有1个1.

解法是Knuth提出的X算法:

1.矩阵被全部删除,搜索成功退出。

2.选择包含元素最少的一列c(可以随便选一列)删除,枚举这列含1的行r作为解的一部分,删除r行所有含1的列。

3.递归调用,成功返回,失败则回溯。

一般搜索中用bool数组标记行和列是否被删除,通过列找所有含1的行需要r次,通过行找所有的列需要c次,

然而在搜索过程中,矩阵的行和列不断被删除,不断减少,后面含1的行列很少,成为稀疏矩阵,

这时再使用r或c次的查找就是浪费时间了。

Dancing Links就是使用双向循环十字链表来存储矩阵,搜索过程中链表大小会不断减小,遍历一次就不需要r或c次了。

双向链表的删除操作:

L[R[x]] = L[x]; R[L[x]] = R[x];

双向链表的恢复操作也很简单:

L[R[x]] = x; R[L[x]] = x;

Knuth将支持这个操作的链表命名为Dancing Links 。

Sudoku 数独向精确覆盖问题的转换

数独有4个约束条件:

1.在i行只能放一个数字k

2.在j列只能放一个数字k

3.在block(i,j)块只能放一个数字k

4.i行j列只能放一个数字

所以建一个01矩阵:

行数n*n*n,n*n个格子,每个格子有n中可能,每种可能对应一行。

列数4*n*n,代表n*n个格子的4个约束条件。

如果数独i行j列已经有值k,则在(i*n+j)*n+k行插入4个1,列数分别是:

i*n+k-1

n*n+j*n+k-1

2*n*n+block(i,j)*n+k-1

3*n*n+i*n+j

否则插入n行,k=1 to n。

如果n=16,那么有4096行,1024列,矩阵遍历需要4096*1024=4194304次,

但是1的个数只有4096*4=16384个,Dancing Links 遍历只需要16384次。

这样对比下就可以看出Dancing Links 节约了很多时间。

调用DLX算法就可以求出数独的一个解或者判断无解。

----------------------------------------------------------------------------------------------------------

Dancing links中文版:http://wenku.baidu.com/view/b3f6fa868762caaedd33d47a.html

Dancing links在搜索中的应用:http://wenku.baidu.com/view/4ab7bd00a6c30c2259019eae.html?from=rec&pos=0&weight=31&lastweight=5&count=5

 
第二次搜索的时候我更新了答案导致了WA,直到一个小时后才发现。。。
 
代码:
#include<cstdio>
#include<cstring>
#define N 4099
#define M 1029

const int m=3,n=9;
int tt,H=4*n*n,cnt,size[M],ans[15][15];
struct Node
{
    int r,c;
    Node *U,*D,*L,*R;
}node[N*M],row[N],col[M],head;
void init(int r,int c)
{
    cnt=0;
    head.r=r;
    head.c=c;
    head.L=head.R=head.U=head.D=&head;
    for(int i=0;i<c;i++)
	{
        col[i].r=r;
        col[i].c=i;
        col[i].L=&head;
        col[i].R=head.R;
        col[i].U=col[i].D=col[i].L->R=col[i].R->L=&col[i];
        size[i]=0;
    }
    for(int i=r-1;i>=0;i--)
	{
        row[i].r=i;
        row[i].c=c;
        row[i].U=&head;
        row[i].D=head.D;
        row[i].L=row[i].R=row[i].U->D=row[i].D->U=&row[i];
    }
}
void insert(int r,int c)
{
    Node *p=&node[cnt++];
    p->r=r;
    p->c=c;
    p->R=&row[r];
    p->L=row[r].L;
    p->L->R=p->R->L=p;
    p->U=&col[c];
    p->D=col[c].D;
    p->U->D=p->D->U=p;
    ++size[c];
}
void delLR(Node *p)
{
    p->L->R=p->R;
    p->R->L=p->L;
}
void delUD(Node *p)
{
    p->U->D=p->D;
    p->D->U=p->U;
}
void resumeLR(Node *p)
{p->L->R=p->R->L=p;}
void resumeUD(Node *p)
{p->U->D=p->D->U=p;}
void cover(int c)
{
    if(c==H)
        return;
	Node *R,*C;
    delLR(&col[c]);
    for(C=col[c].D;C!=&col[c];C=C->D)
		for(R=C->L;R!=C;R=R->L)
		{
			--size[R->c];
			delUD(R);
		}
}
void resume(int c)
{
    if(c==H)
        return;
    Node *R,*C;
    for(C=col[c].U;C!=&col[c];C=C->U)
		for(R=C->R;R!=C;R=R->R)
		{
			++size[R->c];
			resumeUD(R);
		}
    resumeLR(&col[c]);
}
int num,tar,map[15][15],block[15][15];
int dfs(int k)
{
    if(head.L==&head)
	{
		num++;
		if(num==tar)//搜索两次成功
			return 1;
		return 0;
	}
    int INF=-1u>>1,r,c=-1;
	Node *p,*rc;
    for(p=head.L;p!=&head;p=p->L)
        if(size[p->c]<INF)
            INF=size[c=p->c];
	if(!INF)
		return 0;
    cover(c);
    for(p=col[c].D;p!=&col[c];p=p->D)
	{
        for(rc=p->L;rc!=p;rc=rc->L)
            cover(rc->c);
		r=p->r-1;
		if(num==0)
			ans[r/(n*n)][r/n%n]=r%n;
        if(dfs(k+1))
            return 1;
        for(rc=p->R;rc!=p;rc=rc->R)
            resume(rc->c);
    }
    resume(c);
    return 0;
}
int dir[4][2]={-1,0,0,1,1,0,0,-1};
void dfs(int x,int y)//预处理每个格子所属块
{
	block[x][y]=num;
	int i,xx,yy;
	for(i=0;i<4;i++)
		if((map[x][y]&(1<<(i+4)))==0)
		{
			xx=x+dir[i][0];
			yy=y+dir[i][1];
			if(xx>=0&&yy>=0&&xx<n&&yy<n&&block[xx][yy]==-1)
				dfs(xx,yy);
		}
}
void insert(int i,int j,int k)
{
	int r=(i*n+j)*n+k;
	insert(r,i*n+k-1);
	insert(r,n*n+j*n+k-1);
	insert(r,2*n*n+block[i][j]*n+k-1);
	insert(r,3*n*n+i*n+j);
}
void Sudoku()
{
	int i,j,k;
	init(n*n*n+1,H);
	for(i=0;i<n;i++)
		for(j=0;j<n;j++)
			scanf("%d",&map[i][j]);
	memset(block,-1,sizeof(block));
	num=0;
	for(i=0;i<n;i++)
		for(j=0;j<n;j++)
			if(block[i][j]==-1)//预处理每个格子所属块
			{
				dfs(i,j);
				num++;
			}
	for(i=0;i<n;i++)
		for(j=0;j<n;j++)
		{
			k=map[i][j]&15;//取低4位
			if(k)
				insert(i,j,k);
			else
				for(k=1;k<=n;k++)
					insert(i,j,k);
		}
	num=0;
	tar=2;
	dfs(0);
	printf("Case %d:\n",++tt);
	if(!num)
		puts("No solution");
	if(num==2)
		puts("Multiple Solutions");
	if(num==1)
	{
		for(i=0;i<n;i++)
		{
			for(j=0;j<n;j++)
				printf("%d",ans[i][j]+1);
			puts("");
		}
	}
}
int main()
{
	int t;
	scanf("%d",&t);
    while(t--)
		Sudoku();
}

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值