背景
这题其实是 \(M \times N Puzzle\) , \(POJ2893\) 的弱化版,李煜东,北京大学数据结构与算法考试, \(CH0503\)。
题意
给定 \(n \times n\) ( \(n\) 为奇整数, \(3 \leqslant n \leqslant 500\) )的棋盘,上面有 \(n \times n-1\) 个数和一个空格。给出前后两个局面,每次仅允许交换空格和其四周相邻的一个数(如果有的话),问是否有办法将第一个局面移动为第二个局面。
解法
将棋盘上的数一行一行取出排成一列(空格不取),可以得到一个序列。
思考操作的数学意义。左右交换空格和数字对该序列的逆序对数毫无影响。而上下交换空格和数字则会交换序列中 \(n-1\) 个数的位置。又由于 \(n\) 为奇整数,所以 \(n-1\) 必为偶整数,所以交换前后逆序对数的变化一定为偶数。具体证明过程较长,这里不再展开。这也就意味着两个局面能够从一个操作成另一个当且仅当两个局面对应的序列的逆序对数奇偶性相同,否则一定不能转化。
然后对两局面归并排序求逆序对数即可。时间复杂度为 \(O(n^2)\) 。(瓶颈在读入)树状数组求逆序对的时间复杂度是 \(O((n+m)\) \(log\) \(n)\) ( \(m\) 为数值范围大小),本题中 \(m = 249001\) ,故也可以通过本题。喜欢写哪个就写哪个呗 \(QAQ\)
\(trick\)
\(1.\) 对网格图的操作没有头绪时,将其转化为序列操作,思考数学意义。
\(2.\) 左右交换空格和数字对该序列的逆序对数无影响,而上下交换空格和数字则会交换序列中 \(n-1\) 个数的位置,交换前后逆序对数的变化一定为偶数。(重要结论,牢记!!!)
细节
\(1.\) 本题的排序范围是 \(1 \sim n \times n-1\) 而不是 \(1 \sim n\) 。(好傻逼的错误啊)
\(2.\) 多组数据读入时注意清空。(好傻逼的错误啊)
代码
$View$ $Code$
//省略头文件 using namespace std; inline int read() { int ret=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { ret=(ret<<1)+(ret<<3)+ch-'0'; ch=getchar(); } return ret*f; } int n,tmp,k,rr,a[249005],b[249005],c[249005],ans1,ans2; void msort1(int l,int r,int mid) { if(l==r) return; msort1(l,mid,(l+mid)>>1); msort1(mid+1,r,(mid+1+r)>>1); int i=l,j=mid+1; for(register int k=l;k<=r;k++) { if(j>r||(i<=mid&&a[i] >1); msort2(mid+1,r,(mid+1+r)>>1); int i=l,j=mid+1; for(register int k=l;k<=r;k++) { if(j>r||(i<=mid&&b[i] >1); msort2(1,rr,(1+rr)>>1); if((ans1+ans2)&1) puts("NIE"); else puts("TAK"); } return 0; }