4021 24 Puzzle
题目意思:给你一个如上图的图,格子从左到右从上到下编号,每个格子上有数字(0~23,0表示空),操作的过程为当一个格子的上下左右有空格子时可以交换两个格子的数字。
现在给你两个序列,表示的是对应编号上的数字,问能不能通过若干次操作将第一种序列变为第二个。
分析:首先考虑比较特殊的几个位置即最边上的格子,可以它们只能与一个格子交换所以先预处理一下,把能交换的交换,然后判一下两个序列这些位置上的数字是不是都相同,如果不想同那么最终不可以转化。那么剩下的就是4*4的方格了,其中有一个为空,就是15数码了。根据八数码判断能不能完成我们知道与当前状态的逆序数的奇偶有关。可以发现左右两个格子交换时是不改变逆序数的,只有上下交换时才会改变逆序数,无非是向前移动了n-1或相后移动n-1个位置(这里n=4),所以当n为奇数时上下交换逆序数的奇偶性不会改变,当n为偶数时则每一次上下交换,逆序数奇偶性改变。那么算法就出来了,计算一下两个序列对应的逆序数如果两个的逆序数奇偶性相同就可以转化否则不能。
1 #include <iostream> 2 #include<cstdio> 3 #include <cstring> 4 #include <map> 5 #include <algorithm> 6 using namespace std; 7 int a[30]; 8 int b[30]; 9 int mapa[20]; 10 int mapb[20]; 11 int pos[]= {0, 1, 2, 7, 16, 21, 22, 23}; 12 int swappos[]= {3, 6, 3, 6, 17, 20, 17, 20}; 13 bool work() 14 { 15 for(int i=0; i<8; ++i) 16 { 17 if(a[pos[i]]==0)swap(a[pos[i]],a[swappos[i]]); 18 if(b[pos[i]]==0)swap(b[pos[i]],b[swappos[i]]); 19 if(a[pos[i]]!=b[pos[i]])return false; 20 } 21 int l=0; 22 for(int i=0; i<24; ++i) 23 { 24 int f=0; 25 for(int j=0; j<8; ++j) 26 if(i==pos[j]) 27 { 28 f=1; 29 break; 30 } 31 if(f)continue; 32 mapa[l]=a[i]; 33 mapb[l]=b[i]; 34 l++; 35 } 36 int posa=-1,posb=-1; 37 int suma=0; 38 for(int i=1; i<16; ++i) 39 { 40 if(mapa[i]==0) 41 { 42 posa=i; 43 continue; 44 } 45 for(int j=0; j<i; ++j) 46 if(mapa[j]>mapa[i])suma++; 47 } 48 int sumb=0; 49 for(int i=1; i<16; ++i) 50 { 51 if(mapb[i]==0) 52 { 53 posb=i; 54 continue; 55 } 56 for(int j=0; j<i; ++j) 57 if(mapb[j]>mapb[i])sumb++; 58 } 59 int step = abs(posa/4 - posb/4); 60 step=abs(suma-sumb+step); 61 if(step&1)return false; 62 else return true; 63 } 64 int main () 65 { 66 int t; 67 scanf("%d",&t); 68 while(t--) 69 { 70 for(int i=0; i<24; ++i)scanf("%d",&a[i]); 71 for(int i=0; i<24; ++i)scanf("%d",&b[i]); 72 if(work())printf("N "); 73 else printf("Y "); 74 } 75 }