DLX精确覆盖:对于给定的01矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1
数独转换为DLX精确覆盖求解:
每个格子都要填入数字---001到081列,表示数独中9*9=81个格子是否填入数字。如果是,则选取的行在该列上为1。
每一行都要有1~9填入---082到162列,每9列就代表数独中的一行,如果该行有某个数字,则其对应的列上为1
每一列都要有1~9填入---163到243列,每9列就代表数独中的一列,如果该列有某个数字,则其对应的列上为1
每一宫都要有1~9填入---244到324列,每9列就代表数独中的一宫,如果该宫有某个数字,则其对应的列上为1
#include<map>
#include<set>
#include<ctime>
#include<stack>
#include<cmath>
#include<queue>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 9;
const int maxn = N*N*N+10;
const int maxm = N*N*4+10;
const int maxnode = maxn*4+maxm+10;
char g[N+5][N+5];
struct DLX
{
int n,m,sizes,H[maxn],S[maxm],ansd,ans[maxn];
int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
void init( int _n , int _m )
{
n = _n;
m = _m;
for ( int i=0 ; i<=m ; i++ )
{
S[i] = 0;
U[i] = D[i] = i;
L[i] = i-1;
R[i] = i+1;
}
R[m] = 0; L[0] = m; sizes = m;
for ( int i=1 ; i<=n ; i++ ) H[i] = -1;
}
void Link( int r , int c )
{
++S[Col[++sizes]=c];
Row[sizes] = r;
D[sizes] = D[c];
U[D[c]] = sizes;
U[sizes] = c;
D[c] = sizes;
if ( H[r]<0 ) H[r] = L[sizes] = R[sizes] = sizes;
else
{
R[sizes] = R[H[r]];
L[R[H[r]]] = sizes;
L[sizes] = H[r];
R[H[r]] = sizes;
}
}
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 Resume( int c )
{
for ( int i=U[c] ; i!=c ; i=U[i] )
for ( int j=L[i] ; j!=i ; j=L[j] )
++S[Col[U[D[j]]=D[U[j]]=j]];
L[R[c]] = R[L[c]] = c;
}
bool Dance( int d )
{
if ( R[0]==0 )
{
for ( int i=0 ; i<d ; i++ ) g[(ans[i]-1)/9/9][(ans[i]-1)/9%9] = (ans[i]-1)%9+'1';
for ( int i=0 ; i<N ; i++ )
{
for ( int j=0 ; j<N ; j++ )
printf ( "%c" , g[i][j] );
printf ( "\n" );
}
return true;
}
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]);
if ( Dance(d+1) ) return true;
for ( int j=L[i] ; j!=i ; j=L[j] ) Resume(Col[j]);
}
Resume( c );
return false;
}
}dlx;
//i行j列填入k,需要在01表上那些位置填入1
void Place( int &r , int &c1 , int &c2 , int &c3 , int &c4 , int i , int j , int k )
{
r = (i*N+j)*N+k; c1 = i*N+j+1; c2 = N*N+i*N+k;
c3 = N*N*2+j*N+k; c4 = N*N*3+((i/3)*3+(j/3))*N+k;
}
int main()
{
for ( int T ; scanf ( "%d" , &T )==1 ; )
{
for ( int cas=1 ; cas<=T ; cas++ )
{
if ( cas!=1 ){ char s[10]; scanf ( "%s" , s ); printf ( "%s\n" , s ); }
for ( int i=0 ; i<N ; i++ ) scanf ( "%s" , g[i] );
dlx.init( N*N*N , N*N*4 );
int r,c1,c2,c3,c4;
for ( int i=0 ; i<N ; i++ )
for ( int j=0 ; j<N ; j++ )
for ( int k=1 ; k<=N ; k++ )
if ( g[i][j]=='?'||g[i][j]=='0'+k )
{
Place( r , c1 , c2 , c3 , c4 , i , j , k );
dlx.Link( r , c1 );
dlx.Link( r , c2 );
dlx.Link( r , c3 );
dlx.Link( r , c4 );
}
if ( !dlx.Dance(0) ) printf ( "impossible\n" );
}
}
return 0;
}