在一个N*N的矩阵里,填入0,1,2直到N*N-1,当一个数的上下左右四个方向中有一个方向的相邻的数为0时,该数就可以移动到相应位置。给定一个初始矩阵和一个目标矩阵。问能否通过上述操作,将初始矩阵变换为目标矩阵。
这类问题主要是从逆序数奇偶性的角度去考虑,将矩阵内的数按照从左往右从上往下的顺序写成一个数列(0除外),然后求出这个数列的逆序数(数对两数为降序排列的数对总数)。对于左右移操作,他们不改变逆序数,对于上下移操作,若N-1为偶数,不改变奇偶性,若为奇数则改变奇偶性。因此若N-1为偶数,只要目标矩阵与初始矩阵逆序数相同既可以实现变换。若N-1为奇数,则初始矩阵与目标矩阵中0的行数差(每移一行奇偶性变化一次)加初始逆序数若与目标逆序数同奇偶,则可以完成转换。
就这个题而言,还多了一步。由于八个角为死角,别的数移不进去,所以先将里面的零移出,如果这以后八个死角的值不相等则肯定不能完成转换。若想等,则转化为N数码问题(即上述)。
代码如下:
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
int Hash1[8]={0,1,2,7,16,21,22,23};
map<int,int> Hash2;
int S1[8],T1[16],S2[8],T2[16];
void swap(int& a,int& b){
int c=a;a=b;b=c;
}
void read(int* a,int* b){
for(int i=0,j=0,k=0;i<24;++i){
if(Hash2.find(i)==Hash2.end()) scanf("%d",&b[j++]);
else scanf("%d",&a[k++]);
}
}
void deal(int *a,int* b){
for(int i=0;i<8;++i){
if(a[i]!=0) continue;
switch(i){
case 0:case 2:swap(a[i],b[0]);break;
case 1:case 3:swap(a[i],b[3]);break;
case 4:case 6:swap(a[i],b[12]);break;
case 5:case 7:swap(a[i],b[15]);break;
}
}
}
int main(){
Hash2[0]=0;Hash2[1]=1;Hash2[2]=2;Hash2[7]=3;
Hash2[16]=4;Hash2[21]=5;Hash2[22]=6;Hash2[23]=7;
int t,cnt1,cnt2,r1,r2;scanf("%d",&t);
bool flag;
while(t--){
flag=true;cnt1=cnt2=0;
read(S1,T1);read(S2,T2);
deal(S1,T1);deal(S2,T2);
for(int i=0;i<8;++i){
if(S1[i]!=S2[i]){flag=false;break;}
}
if(!flag){printf("Y\n");continue;}
for(int i=0;i<16;++i){
for(int j=i+1;j<16;++j){
if(T1[i]!=0&&T1[j]!=0&&T1[i]>T1[j]) ++cnt1;
if(T2[i]!=0&&T2[j]!=0&&T2[i]>T2[j]) ++cnt2;
}
}
for(int i=0;i<16;++i){
if(T1[i]==0) r1=i/4;
if(T2[i]==0) r2=i/4;
}
if((cnt1+cnt2+r1-r2)%2) printf("Y\n");
else printf("N\n");
}
return 0;
}