这题绝对是一个大坑……
题意是,有一个拼图有9块,每一块的四条边都可能有齿轮。每次你可以选择把一块顺时针转90度,但是当你转这个的时候,其相邻的图块如果和它相邻的边两边都有齿轮的话,就会咬合,使得旁边的图块也跟着往相反的方向转,同时这种影响是传递的,也就是说你转一个图块,可能9块都跟着动。
这道题实现起来,有非常多的优化细节要注意:
1. 千万不要直接去转8*8的那个字符矩阵,这样复杂度太高了。正确的方法是,由于每个图块只有4种状态,所以我们可以预先把初态和目标状态的每一块单独比较(不考虑咬合),记录下对应位置的图块转几次能转出目标位置的样子。于是你会发现,实际上状态只有4^9种,其实并不大,我们接下来就用0123表示每个格的状态,这样就大大地压缩了一步;
但是需要注意的是,目标状态可能有很多种,因为有可能一块图是对称的,比如样例中间那块,转0、1、2、3次都可以。所以这个都要记录下来。
2. 接下来你会发现这个事情用BFS就可以解决了,但是仔细考虑一下,状态复杂度4^9,转移复杂度9(选择)*9(操作),还是挺紧的……
3. 对于四边齿轮的处理,千万不要把它记录到你的格子结构体里去,因为这样当你转格子的时候还需要额外维护这个数组,增加了复杂度,正确的方法是单开一个数组,就记录初始的状态,并且将其用二进制表示,这样,你知道每个格子转了多少次,你就能通过几步位运算知道这个格子四周的齿轮是什么样子了。
4. 由于转一个格子可能联动转很多个格子,BFS里面还需要套一个DFS/BFS,用于判断每个格子应该怎么转。省时间的方法是这里开一个栈,记录都哪些格子需要转,这样能省些时间,不用每次都把所有9个格子都看一遍。
5. 另一个省时间的办法是,当你预处理完所有的终结状态以后,直接DFS一下,将所有的终止状态的数值记录下来,这样每次你比对的时候直接O(1)就完成了;
6. 还有一个细节是,每次从队列里取出一个整数状态后解码,之后真正要转的时候可以直接对整数进行位操作,方法就是取出那一位,改了之后再塞回去。这样就不用操作数组的那一位,之后再算hash值了。在这么大的基数下,一点不对可能就会TLE。
实现上也有些细节,关键就在于STL的queue。首先queue是没有清零函数的,要清零就只能一个一个pop,在这道题里慢得跟蜗牛一样,大BFS里要是用的话千万不要开在全局变量里,把BFS弄成一个函数,用一个申请一个。另外就是STL的queue申请是【很慢的】!所以小BFS里千万不要用queue,我T了两天就T在这里…… 所以我才算明白为什么大家都用DFS,之后我手写了一个队列瞬间就出来了。总之,一定避免频繁地申请queue,偶尔用用还行。
最后就是这个代码里凡是a[5][5]的地方,我原来开a[4][4],但是死活segmentation fault,我也查不出哪儿可能访问越界,之后改成5就能过了……我也不知道怎么搞的……
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
struct Panel
{
char c[10][10];
void spin(bool clockwise)
{
char tmp[10][10]={};
if(clockwise)
{
int i,j;
for(i=1; i<=8;i++)
{
for(j=1;j<=8;j++)
{
tmp[j][9-i] = c[i][j];
}
}
memcpy(c,tmp,sizeof(tmp));
}
else
{
int i,j;
for(i=1; i<=8; i++)
{
for(j=1; j<=8; j++)
{
tmp[j][i] = c[i][j];
}
}
memcpy(c, tmp, sizeof(tmp));
}
}
bool operator ==(const Panel &b) const
{
int i,j;
for(i=1;i<=8;i++)
{
for(j=1;j<=8;j++)
{
if(c[i][j] != b.c[i][j])
return false;
}
}
return true;
}
bool operator !=(const Panel &b) const
{
int i,j;
for(i=1;i<=8;i++)
{
for(j=1;j<=8;j++)
{
if(c[i][j] != b.c[i][j])
return true;
}
}
return false;
}
};
struct State
{
Panel panel[5][5];
};
int two[30]={0};
int cp[5][5];
int toNum(int a[5][5])
{
int sum=0;
int i,j;
for(i=1;i<=3;i++)
{
for(j=1;j<=3;j++)
{
sum += two[2* ( (i-1)*3 + (j-1) )] * a[i][j];
}
}
return sum;
}
State state;
State final;
const int dx[4]={1,0,-1,0};
const int dy[4]={0,1,0,-1};
const int trans[4] = {2,3,0,1};
int final_state[5][5][10];
int p_final_state[5][5];
bool state_mark[1000000];
pair<int, int> q[500000];
int head=0, tail=0;
int link[5][5];
int finish[1000000];
void make_finish(int now, int sum)
{
if(now == 0)
{
finish[sum] = 1;
return;
}
int i;
int x = (now-1)/3+1;
int y = (now-1)%3+1;
for(i=0;i<p_final_state[x][y] ;i++)
{
make_finish(now-1, sum*4 + final_state[x][y][i]);
}
return;
}
void decode(int ret[5][5], int code)
{
int i=1, j=1;
while(code>0)
{
ret[i][j] = code%4;
code/=4;
j++;
if(j==4)
{
j=1;
i++;
}
}
return;
}
void push(pair<int, int> p)
{
q[tail++] = p;
return;
}
pair<int, int> front()
{
pair<int, int> ret = q[head];
head++;
return ret;
}
void input(State &now)
{
int i,j;
for(i=1; i<=24;i++)
{
for(j=1;j<=24;j++)
{
char tmp;
scanf("%c", &tmp);
if(tmp!='#' && tmp!='.')
{
j--;
continue;
}
now.panel[(i-1)/8+1][(j-1)/8+1].c[(i-1)%8+1][(j-1)%8+1] = tmp;
}
}
return;
}
int calc_link(int x, int y, int times) // 0111 1011 1101 1110 order = 3210
{
int ret = link[x][y];
int tmp = ret & (two[times]-1);
ret = ret >> times;
ret += tmp << (4 - times);
return ret;
}
struct Q_node
{
int x;
int y;
int color;
Q_node(){}
Q_node(int _x, int _y, int _color)
{
x=_x;
y=_y;
color=_color;
}
};
Q_node stack[40];
int p_stack=0;
Q_node ds_q[40];
int ds_head=0, ds_tail=0;
void init()
{
head=0;
tail=0;
memset(cp,0,sizeof(cp));
memset(finish,0,sizeof(finish));
memset(state_mark,0,sizeof(state_mark));
memset(link, 0, sizeof(link));
memset(final_state,0, sizeof(final_state));
memset(p_final_state,0,sizeof(p_final_state));
memset(stack, 0, sizeof(stack));
p_stack = 0;
ds_head=ds_tail=0;
memset(link, 0, sizeof(link));
return;
}
void determine_spin(int now_state[5][5], int nowi, int nowj)
{
ds_head=0;
ds_tail=0;
int mark[5][5]={0};
memset(stack, 0, sizeof(stack));
p_stack = 0;
Q_node start(nowi, nowj, 1);
ds_q[ds_tail++] = start;
mark[nowi][nowj] = 1;
stack[++p_stack] = start;
while(ds_tail != ds_head)
{
Q_node now = ds_q[ds_head++];
int link_now = calc_link(now.x, now.y, now_state[now.x][now.y]);
int i;
for(i=0;i<=3;i++)
{
if ( (link_now & two[i]) == 0 )
continue;
int i_adj = now.x + dx[i];
int j_adj = now.y + dy[i];
int link_adj = calc_link(i_adj, j_adj, now_state[i_adj][j_adj]);
if(mark[i_adj][j_adj] ==0 && (link_adj & two[ trans[i] ] )!=0 )
{
mark[i_adj][j_adj]=1;
Q_node node(i_adj, j_adj, -now.color);
stack[++p_stack] = node;
ds_q[ds_tail++] = node;
}
}
}
return;
}
int t;
int main()
{
two[0]=1;
for(int tmp_i=1; tmp_i<=20; tmp_i++)
two[tmp_i] = two[tmp_i-1] *2;
scanf("%d", &t);
int files;
for(files=1; files<=t; files++)
{
init();
input(state);
input(final);
int i,j;
for(i=1;i<=9;i++)
{
for(j=0;j<=3;j++)
{
int tmp;
scanf("%d", &tmp);
link[(i-1)/3+1][(i-1)%3+1] = (link[(i-1)/3+1][(i-1)%3+1] << 1) + tmp;
}
}
bool ok=true;
for(i=1;i<=3;i++)
{
for(j=1;j<=3;j++)
{
for(int k=1; k<=4;k++)
{
state.panel[i][j].spin(true);
if(state.panel[i][j] == final.panel[i][j])
{
final_state[i][j][ p_final_state[i][j]++ ] = k%4;
}
}
if(p_final_state[i][j] == 0)
{
printf("-1\n");
ok=false;
break;
}
}
if(!ok)
break;
}
if(!ok)
continue;
make_finish(9,0);
int first_num = toNum(cp);
if(finish[ first_num ]==1)
{
printf("0\n");
continue;
}
push( make_pair(first_num, 0) );
state_mark[first_num] = true;
bool found = false;
while(head!=tail)
{
pair<int, int> now = front();
int now_state[5][5]={};
decode(now_state, now.first);
int mover = now.first;
for(i=1; i<=3; i++)
{
for(j=1; j<=3; j++)
{
determine_spin(now_state,i,j);
int i_stack;
for(i_stack =1; i_stack<=p_stack; i_stack++)
{
int i_spin = stack[i_stack].x, j_spin = stack[i_stack].y;
int spin_color = stack[i_stack].color;
int digit_num = (i_spin-1) * 3 + j_spin-1;
int this_digit = (mover/two[2*digit_num])%4;
mover -= this_digit * two[2*digit_num];
this_digit = ( (this_digit + spin_color) %4+4)%4;
mover += this_digit * two[2*digit_num];
}
//push
int next_state = mover;
if(state_mark[next_state]!=true)
{
if(finish[next_state]==1)
{
printf("%d\n", now.second+1);
found = true;
break;
}
state_mark[next_state]=true;
push( make_pair(next_state, now.second + 1) );
}
mover = now.first;
}
if(found)
break;
}
if(found)
break;
}
if(!found)
printf("-1\n");
}
//system("pause");
return 0;
}