题意
一个16×16的数独(每一行,每一列都是包含了'0' ~ 'F' 16个字符),将矩阵分成4×4块,每块4×4,旋转其中一些方块
现在给你旋转后的矩阵,问最少顺时针旋转其中某些块多少次,能够还原成一个完整的数独
分析
这个题的精髓在于设置状态。
用整数中的二进制位位表示某一个数字有没有出现,
由于旋转只针对于4×4的矩阵,所以我们将4×4的子矩阵设为元素,一共16个元素,每个元素维护一下4种旋转情况下,每一行和每一列的二进制标志位(4个1,12个0)
从第一块开始深搜每一个元素的4种状态,得到最小答案
两个剪枝地方:如果当前旋转次数大于当前维护的最小状态,返回 因为是从左到右从上到下填充,所以如果上面的某一行不满足一列包含16个不同字符,那么直接返回
代码
#include<bits/stdc++.h>
using namespace std;
struct cre{
int heng[4],shu[4];
}a[4][4][4];
char maps[20][20];
int H[16],S[16];
const int pp = (1<<16)-1;
int zh(char c)
{
if(c<='9'&& c>='0') return c-'0';
else return c-'A'+10;
}
void cl(int sx,int sy,int x,int y)
{
for(int i = sx,id=0;i<sx+4;i++,id++)
for(int j = sy;j<sy+4;j++)
a[0][x][y].heng[id] |= 1<<(zh(maps[i][j]));
for(int j = sy,id=0;j<sy+4;j++,id++)
for(int i = sx;i<sx+4;i++)
a[0][x][y].shu[id] |= 1<<(zh(maps[i][j]));
for(int i = 1;i<4;i++){
a[i][x][y].heng[0] = a[i-1][x][y].shu[0];
a[i][x][y].heng[1] = a[i-1][x][y].shu[1];
a[i][x][y].heng[2] = a[i-1][x][y].shu[2];
a[i][x][y].heng[3] = a[i-1][x][y].shu[3];
a[i][x][y].shu[0] = a[i-1][x][y].heng[3];
a[i][x][y].shu[1] = a[i-1][x][y].heng[2];
a[i][x][y].shu[2] = a[i-1][x][y].heng[1];
a[i][x][y].shu[3] = a[i-1][x][y].heng[0];
}
}
int ans = 0x7fffffff;
bool pd()
{
for(int i = 0;i<16;i++) if(H[i]!=pp || S[i]!=pp) return false;
return true;
}
void dg(int X,int Y,int tot)
{
if(ans<tot) return;
if(X && Y == 0){
for(int i = 4*(X-1);i<4*X;i++){
if(H[i]!=pp) return ;
}
}
if(X == 4){
if(pd()) ans = tot;
return ;
}
int NX =X ,NY = Y+1;
if(NY == 4) NY = 0,NX++;
for(int now =0 ;now<=3;now++)
{
for(int i = X*4,id = 0;i<X*4+4;i++,id++) H[i]^=a[now][X][Y].heng[id];
for(int i = Y*4,id = 0;i<Y*4+4;i++,id++) S[i]^=a[now][X][Y].shu[id];
dg(NX,NY,tot+now);
for(int i = X*4,id = 0;i<X*4+4;i++,id++) H[i]^=a[now][X][Y].heng[id];
for(int i = Y*4,id = 0;i<Y*4+4;i++,id++) S[i]^=a[now][X][Y].shu[id];
}
}
int main()
{
int _;scanf("%d",&_);
while(_--)
{
memset(a,0,sizeof(a));
memset(H,0,sizeof(H));
memset(S,0,sizeof(S));
ans = 0x7fffffff;
for(int i =0;i<16;i++)
for(int j = 0;j<16;j++)
scanf(" %c",&maps[i][j]);
for(int i = 0,ii=0;i<16;i+=4,ii++)
for(int j = 0,jj=0;j<16;j+=4,jj++)
cl(i,j,ii,jj);
dg(0,0,0);
printf("%d\n",ans);
}
}