您的位置:首页 > 产品设计 > UI/UE

【dfs预处理+DLX】hdu 4069 Squiggly Sudoku

2015-09-05 16:06 465 查看
Source :hdu 4069 Squiggly Sudoku

http://acm.hdu.edu.cn/showproblem.php?pid=4069

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)

题意

9X9的数独游戏,满足每行、每列、每宫填满1-9,注意“宫”的形状变化了!

示例

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

思路

dfs预处理给所有的小格确定所属的方阵编号,然后DLX精确覆盖即可,这题收获很大,有以下三点:

1.dlx转换为精确覆盖问题时,列任务有4个,分别构成N×N×4列,其中sub(a,b)表示第a个子方阵要有字母b,普通的数独游戏根据元素(r,c)在第r行第c列就可以确定属于哪个a方阵,这道题特殊就特殊在方阵形状变了,你需要重新给(r,c)所属方阵编号了,怎么做?dfs所有点,利用位运算判断是否越界,预处理得到所有方格(i,j)所属方阵编号block[ i ][ j ];再构造724×324矩阵时把sub(a,b)替换为block[ r ][ c ]就ok了!

2.难点!做着做着你会发现,解的个数与最终的解不可兼得,因为回溯!所以中间的答案没有得到保留。怎么破?!当解的个数为1时,把答案ans存起来,最终要是解的个数就是1,直接输出刚才保存的结果,否则就是多解,我们也不用把所有的结果都存起来了。

3.成也剪枝!结果没问题,但最终还是TLEQAQ,什么情况?处理解的个数,当为2时就return好了,不用继续dlx了,做题和解决问题还是有点区别啊Orz..

补充:如果遇到要求出所有的解,每到递归基就打印一次,没必要存储所有结果了!模板dlx输出的是第一组可行解,至于有没有其他的不知道,把dfs里面的return去掉就可以了。



参考代码

/*====================================*\
|*     舞蹈链    DLX                  *|
\*====================================*/
/*Author:Hacker_vision*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#include<map>
#include<climits>
#define clr(k,v) memset(k,v,sizeof(k))
#define ll long long
#define eps 1e-8
using namespace  std;

const int SLOT = 0;
const int ROW = 1;
const int COL = 2;
const int SUB = 3;
const int N = 9;
const int M = 3;
const int maxr = N*N*N + 10;//行数
const int maxn = N*N*4 + 10;//列数
const int maxnode = maxr*maxn + 10;//总节点数
int tot,g[12][12];
int top,temp[maxr];

struct DLX{
int n,sz;//列数,节点总数
int S[maxn];//各列节点数
int row[maxnode],col[maxnode];//各节点行列编号
int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//十字链表,用数组写的
int ansd,ans[maxr];//解
void init(int n){ //n是列数
this->n = n;
//虚拟节点
for(int i = 0; i <= n; ++ i){
U[i] = i;D[i] = i;L[i] = i - 1;R[i] = i + 1;
}
R
= 0;L[0] = n;
sz = n + 1;
memset(S,0,sizeof(S));
}
void addRow(int r,vector<int>columns){
int first= sz;
for(int i = 0; i < columns.size(); ++ i){
int c = columns[i];
L[sz] = sz - 1;R[sz] = sz + 1;D[sz] = c;U[sz] = U[c];
D[U[c]] = sz;U[c] = sz;
row[sz] = r;col[sz] = c;
S[c]++;sz++;
}
R[sz - 1] = first;L[first] = sz - 1;
}
//顺着链表A,遍历除s外的其他元素
// #define FOR(i,A,s) for(int i = A[s]; i != s;i = A[i])
void remove(int c){
L[R[c]] = L[c];
R[L[c]] = R[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[col[j]];
}
}
void restore(int c){
for(int i = U[c]; i != c; i = U[i])
for(int j = L[i]; j != i; j = L[j]){
++S[col[j]];U[D[j]] = j;D[U[j]] = j;
}
L[R[c]] = c;
R[L[c]] = c;
}
bool dfs(int d){
if(R[0] == 0){
ansd = d;//选了ansd行
tot++;
if(tot == 1){
top = ansd;//ansd是局部变量,拿出来
for(int i = 0; i < ansd; ++ i) temp[i] = ans[i];
}
return true;
}
//找S的最小列
int c = R[0]; //第一个未删除的列
for(int i = R[0];i != 0; i = R[i]) if(S[i] < S[c]) c = i;
remove(c);
for(int i = D[c]; i != c;i = D[i]){
ans[d] = row[i];
for(int j = R[i]; j != i; j = R[j]) remove(col[j]);
dfs(d+1);
if(tot > 1) return true;
for(int j = L[i]; j != i; j = L[j]) restore(col[j]);
}
restore(c);
return false;
}
bool solve(vector<int>&v){//solve函数这里就没用了,因为回溯!
v.clear();
if(!dfs(0)) return false;
for(int i = 0; i < ansd; ++ i) v.push_back(ans[i]);
return true;
}
};

//行、列的统一编解码函数。从1开始编号
int encode(int a,int b,int c){
return a*N*N + b*N + c + 1;
}

void decode(int code,int& a,int& b,int& c){
code--;
c = code%N; code/=N;
b = code%N; code/=N;
a = code;
}
//block[][]也可以当做vis[][]用,比较巧妙
int block[12][12],dir[4][2]={-1,0,0,1,1,0,0,-1};//四个方向,16,32,64,128决定的方向
void dfs(int x,int y){//函数重载,哈哈!
block[x][y] = tot;
for(int i = 0; i < 4; ++ i){
int xx = x + dir[i][0];
int yy = y + dir[i][1];
if(xx>=0&&yy>=0&&xx<N&&yy<N&&block[xx][yy]==-1&&(g[x][y]&(1<<(i+4)))==0){
dfs(xx,yy);                                  //g[x][y]&(1<<(1+4)) == 0,说明g[x][y]没有32,可以往右走,dir方向很重要
}
}
}

DLX dlx;

int main(){
#ifndef ONLINE_JUDGE
freopen("input.txt","r",stdin);
#endif // ONLINE_JUDGE
int T;cin>>T;int cnt=1;
while(T--){
for(int i = 0; i < N; ++ i)
for(int j = 0;j < N; ++ j)
scanf("%d",&g[i][j]);
//预处理,得到block[r][c],确定(r,c)所在方阵编号0-8
clr(block,-1);
tot = 0;//初始化方阵编号
for(int i = 0; i < N; ++ i)//遍历所有点确定所有点的编号,最好情况下有效循环次数9次
for(int j = 0; j < N; ++ j)
if(block[i][j] == -1){
dfs(i,j);
tot++;
}
//跑DLX即可,注意列任务的第4个传参的含义
dlx.init(N*N*4);//列数
for(int r = 0; r < N; ++ r)
for(int c = 0; c < N; ++ c)
for(int v = 0; v < N; ++ v){
int k = g[r][c]&15;//取低四位!!!!!
if(k == 0||k == 1 + v){
vector<int>columns;
columns.push_back(encode(SLOT,r,c));//列数分四种情况,r行c列要有数字
columns.push_back(encode(ROW,r,v));//r行要有数字v
columns.push_back(encode(COL,c,v));//c列要有数字v
columns.push_back(encode(SUB,block[r][c],v));//第x个方阵要有数字v,之前根据(r,c)大小直接确定第几个方阵,现在需要预处理一下,block[r][c]返回(r,c)所属方阵编号
dlx.addRow(encode(r,c,v),columns);//上一步传参,方阵编号(0~8)
}
}
tot = 0;
dlx.dfs(0);
//控制输出
printf("Case %d:\n",cnt++);
// printf("解得个数tot = %d\n",tot);
if(!tot) puts("No solution");
else if(tot > 1) puts("Multiple Solutions");
else{
for(int i = 0; i < top; ++ i){
int r,c,v;
decode(temp[i],r,c,v);
g[r][c] = 1 + v;
}
for(int i = 0; i < N; ++ i){
for(int j = 0; j < N; ++ j)
printf("%d",g[i][j]);
puts("");
}
}
}
return 0;
}


加粗
Ctrl + B


斜体
Ctrl + I


引用
Ctrl + Q


插入链接
Ctrl + L


插入代码
Ctrl + K


插入图片
Ctrl + G


提升标题
Ctrl + H


有序列表
Ctrl + O


无序列表
Ctrl + U


横线
Ctrl + R


撤销
Ctrl + Z


重做
Ctrl + Y
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dlx dfs