题目大意
原有一个16×16的数独,这个数独的某些宫(粗黑线划分的区域)被逆时针的旋转了未知次,给出某个数独被操作后的终态,求从原始状态到终态的最小操作次数。
详见 http://acm.hdu.edu.cn/showproblem.php?pid=6341 题目样例,解释很详细。
解题思路
直接进行枚举状态的搜索虽然加上优化可以过,但是考试的时候还是不太愿意写这种可能被强数据强行卡掉的算法,于是考虑这个数独的性质。
可以确定的是,这个数独的解只有两种:对于已知存在的一组解,把数独的所有宫再额外旋转两次即可得到第二组解;本来考虑题目性质的时候就想到了比较两种解法然后取min作为答案的,但是最后还是忘记写了,导致比赛时候交上去的第一发wa掉了。
首先只考虑每一个横行的宫,不考虑横行与横行之间的影响,每一个宫只有4种状态,分别暴力的枚举一横行四个宫的旋转次数需要 4^4=256 的时间复杂度。把宫的四个边界的存在的数压位预处理出来,这样每次检验是否合法只需要4步位运算。这样处理4个横行至多需要 4*256*4=4096次运算。
处理好每个横行后,横行和横行之间可能相互冲突,所以应该调整掉这些冲突。为了保证在调整的过程中始终保持横行内合法,某个横行只能不转or转两次。这样枚举每个横行是否需要转需要至多 2^4*4=64 次运算。
每个解都可以对应一个镜面解,比较两个解的大小,取min即为答案。
应该是全场最快吧,15ms?
代码
#include <bits/stdc++.h>
using namespace std;
inline char get_val(char ch) {
if('0'<=ch && ch<='9') return ch-'0';
return 10+ch-'A';
}
inline int add(int a,int b) {
if(a+b<4) return a+b;
return a+b-4;
}
#define bit(k) (1<<(k))
const int n=16;
int T;
char s[n+5][n+5];
int grid[n+5][n+5][4];
int rot[n+5][n+5], ROT[n+5];
void init() {
register int i,j,k;
for(i=0;i<n;++i) {
ROT[i]=0;
for(j=0;j<n;++j) {
rot[i][j]=0;
for(k=0;k<4;++k)
grid[i][j][k]=0;
}
}
return;
}
void calc_grid(int x,int y) {
register int i,j;
for(i=x*4;i<(x+1)*4;++i)
for(j=y*4;j<(y+1)*4;++j) {
if(i==4*x) grid[x][y][0]|=bit(s[i][j]);
if(j==4*y) grid[x][y][1]|=bit(s[i][j]);
if(i==4*x+3) grid[x][y][2]|=bit(s[i][j]);
if(j==4*y+3) grid[x][y][3]|=bit(s[i][j]);
}
return;
}
bool check_row(int r) {
int cur1=0;
for(int j=0;j<4;++j) cur1|=grid[r][j][rot[r][j]];
return (cur1==((1<<16)-1));
}
bool dfs_row(int x,int y) {
if(y==4) return check_row(x);
for(int i=0;i<4;++i) {
rot[x][y]=i;
if(dfs_row(x,y+1)) return true;
}
return false;
}
bool check_col(int c) {
int cur=0;
for(int i=0;i<4;++i) cur|=grid[i][c][(rot[i][c]+ROT[i]+1)%4];
return (cur==((1<<16)-1));
}
bool dfs_col(int x,int y) {
if(x==4) return check_col(y);
for(int i=0;i<4;i+=2) {
ROT[x]=i;
if(dfs_col(x+1,y)) return true;
}
return false;
}
void work() {
register int i,j;
init();
for(i=0;i<n;++i) {
scanf("%s",s[i]);
for(j=0;j<n;++j)
s[i][j]=get_val(s[i][j]);
}
for(i=0;i<4;++i)
for(j=0;j<4;++j)
calc_grid(i,j);
for(i=0;i<4;++i)
dfs_row(i,0);
dfs_col(0,0);
for(i=0;i<4;++i) if(ROT[i])
for(j=0;j<4;++j)
rot[i][j]+=ROT[i];
int ans1=0;
for(i=0;i<4;++i)
for(j=0;j<4;++j)
ans1+=rot[i][j]%4;
int ans2=0;
for(i=0;i<4;++i)
for(j=0;j<4;++j)
ans2+=(rot[i][j]+2)%4;
printf("%d\n",min(ans1,ans2));
return;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input0.txt","r",stdin);
#endif
for(scanf("%d",&T);T;T--)
work();
return 0;
}