给定 n 个拼图碎片,现在要用这些碎片拼出一个 4×4 的正方形,要求每个碎片都被用到。若只有一组解,输出方案。
Data Constraint
n≤16
考虑将问题转化成精确覆盖问题。
每个格子看成一列,因为每个碎片都要用到,所以每个碎片再开一列。
每个碎片可能匹配的位置都开一行,可以覆盖的位置对应的列就是矩阵中的1.
然后跑DLX即可。
#include
#include
#include
#include
#include
#include
using namespace std ;
#define N 10000 + 10
vector < int > Ans , Q ;
char ch[5][5] ;
int U[N] , D[N] , L[N] , R[N] , S[N] ;
int Row[N] , Col[N] , Belong[5][5] ;
int Cs[350] , Color[5][5] ;
int n , Cnt , Num ;
void AddRow( int r ) {
int head = Cnt + 1 ;
for (int i = 1 ; i <= Cs[0] ; i ++ ) {
int now = Cs[i] ;
++ Cnt ;
U[Cnt] = U[now] , D[Cnt] = now , L[Cnt] = Cnt - 1 , R[Cnt] = Cnt + 1 ;
D[U[now]] = Cnt , U[now] = Cnt ;
Row[Cnt] = r , Col[Cnt] = now ;
S[now] ++ ;
}
L[head] = Cnt , R[Cnt] = head ;
}
void Remove( int c ) {
R[L[c]] = R[c] ;
L[R[c]] = L[c] ;
for (int x = D[c] ; x != c ; x = D[x] ) {
for (int y = R[x] ; y != x ; y = R[y] ) {
D[U[y]] = D[y] , U[D[y]] = U[y] ;
S[Col[y]] -- ;
}
}
}
void Restore( int c ) {
for (int x = U[c] ; x != c ; x = U[x] ) {
for (int y = L[x] ; y != x ; y = L[y] ) {
S[Col[y]] ++ ;
D[U[y]] = y , U[D[y]] = y ;
}
}
R[L[c]] = c ;
L[R[c]] = c ;
}
void DFS() {
if ( R[0] == 0 ) {
Num ++ ;
if ( Num > 1 ) return ;
Ans = Q ;
return ;
}
int c = R[0] ;
for (int x = R[0] ; x ; x = R[x] ) if ( S[x] < S[c] ) c = x ;
Remove(c) ;
for (int x = D[c] ; x != c ; x = D[x] ) {
Q.push_back(x) ;
if ( x == 222 ) {
x ++ ;
x -- ;
}
for (int y = R[x] ; y != x ; y = R[y] ) Remove(Col[y]) ;
DFS() ;
Q.pop_back() ;
if ( Num > 1 ) return ;
for (int y = L[x] ; y != x ; y = L[y] ) Restore(Col[y]) ;
}
Restore(c) ;
}
void Print() {
int size = Ans.size() ;
for (int i = 0 ; i < size ; i ++ ) {
int now = Ans[i] ;
int w = now ;
for (int x = R[now] ; x != now && Col[w] <= 16 ; x = R[x] ) {
if ( Col[x] > 16 ) w = x ;
}
int type = Col[w] - 16 ;
for (int x = R[w] ; x != w ; x = R[x] ) {
int now = Col[x] ;
int xx = now / 4 + (now % 4 != 0) ;
int yy = now % 4 ? now % 4 : 4 ;
Color[xx][yy] = type ;
}
}
printf( "Yes, only one!\n" ) ;
for (int i = 1 ; i <= 4 ; i ++ ) {
for (int j = 1 ; j <= 4 ; j ++ ) printf( "%d" , Color[i][j] ) ;
printf( "\n" ) ;
}
}
int main() {
freopen( "puzzle.in" , "r" , stdin ) ;
freopen( "puzzle.out" , "w" , stdout ) ;
while ( scanf( "%d" , &n ) != EOF ) {
Ans.clear() ;
memset( U , 0 , sizeof(U) ) ;
memset( D , 0 , sizeof(D) ) ;
memset( L , 0 , sizeof(L) ) ;
memset( R , 0 , sizeof(R) ) ;
memset( S , 0 , sizeof(S) ) ;
memset( Row , 0 , sizeof(Row) ) ;
memset( Col , 0 , sizeof(Col) ) ;
for (int i = 0 ; i <= 16 + n ; i ++ ) {
U[i] = D[i] = i ;
L[i] = i - 1 , R[i] = i + 1 ;
}
L[0] = 16 + n , R[16+n] = 0 ;
Cnt = 16 + n ;
int tot = 0 ;
for (int i = 1 ; i <= 4 ; i ++ )
for (int j = 1 ; j <= 4 ; j ++ ) Belong[i][j] = ++ tot ;
int Rowtot = 0 ;
for (int i = 1 ; i <= n ; i ++ ) {
int r , c ;
scanf( "%d%d" , &r , &c ) ;
for (int j = 1 ; j <= r ; j ++ ) {
scanf( "\n" ) ;
for (int k = 1 ; k <= c ; k ++ ) {
scanf( "%c" , &ch[j][k] ) ;
}
}
for (int x = 1 ; x + r - 1 <= 4 ; x ++ ) {
for (int y = 1 ; y + c - 1 <= 4 ; y ++ ) {
++ Rowtot ;
Cs[0] = 0 ;
Cs[1] = 16 + i ;
for (int j = 1 ; j <= r ; j ++ ) {
for (int k = 1 ; k <= c ; k ++ ) {
if ( ch[j][k] == '0' ) continue ;
Cs[++Cs[0]] = Belong[x+j-1][y+k-1] ;
}
}
Cs[++Cs[0]] = 16 + i ;
AddRow( Rowtot ) ;
}
}
}
Num = 0 ;
DFS() ;
if ( Num == 0 ) { printf( "No solution\n" ) ; continue ; }
if ( Num == 1 ) { Print() ; continue ; }
if ( Num == 2 ) { printf( "Yes, many!\n" ) ; }
}
return 0 ;
}
以上.