题目意思
给你一个T,表示案例数量,给次给4*4块的数独,其中每一块数独都是4*4且不重复的,每一块数独只能顺时针反转,求使得数独合法的最少翻转次数。
直接暴搜加上可行性剪枝和最优性剪枝即可。
数独的限制较强,可行性剪枝的效果很好。
对每一块数独从上到下,从左到右遍历,每遍历一块,判断是否合法,若不合法就旋转一次再判断。
判断过程如下图所示,假设我们现在搜到了红色块区域,我们要对每一条蓝线和绿线查找有没有相同的元素,若有,则红色块不合法,反之,搜索下一块。
这里flag和book作为一个全局变量,flag作为标记每一次查询都是不同的,book数组标记这个数字在第几次查询中被查询到了。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cctype>
using namespace std;
char s[16][16];
int w[4][4];
int ans, flag, book[16];
void rot(int x,int y)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
w[j][3 - i] = s[i + x][j + y];
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
s[i + x][j + y] = w[i][j];
}
}
int check(int a, int b)
{
for (int i = a; i < a + 4; i++)
{
flag++;
for (int j = 0; j < b + 4; j++)
{
if (book[s[i][j]] == flag)
return 0;
book[s[i][j]] = flag;
}
}
for (int j = b; j < b + 4; j++)
{
flag++;
for (int i = 0; i < a + 4; i++)
{
if (book[s[i][j]] == flag)
return 0;
book[s[i][j]] = flag;
}
}
return 1;
}
void dfs(int i, int j, int k)
{
if (i == 4)
{
ans = min(ans, k);
return;
}
if (k >= ans)
return;
if (j == 4)
return dfs(i + 1, 0, k);
for (int t = 0; t < 4; t++)
{
if (check(4 * i, 4 * j))
dfs(i, j + 1, k + t);
rot(4 * i, 4 * j);
}
}
int main()
{
ios::sync_with_stdio(false);
int t;
scanf("%d", &t);
while (t--)
{
for (int i = 0; i < 16; i++)
scanf("%s", s[i]);
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 16; j++)
{
if (isdigit(s[i][j]))
s[i][j] -= '0';
else
s[i][j] -= 'A' - 10;
}
}
ans = 999999;
dfs(0, 0, 0);
printf("%d\n", ans);
}
}