奇數碼問題
時間限制: 1 Sec 內存限制: 128 MB題目描述
你一定玩過八數碼游戲,它實際上是在一個3*3的網格中進行的,1個空格和1~8這8個數字恰好不重不漏地分布在這3*3的網格中。
例如:
5 2 8
1 3 _
4 6 7
在游戲過程中,可以把空格與其上、下、左、右四個方向之一的數字交換(如果存在)。
例如在上例中,空格可與左、上、下面的數字交換,分別變成:
5 2 8 5 2 _ 5 2 8
1 _ 3 1 3 8 1 3 7
4 6 7 4 6 7 4 6 _
奇數碼游戲是它的一個擴展,在一個n*n的網格中進行,其中n為奇數,1個空格和1~n*n-1這n*n-1個數恰好不重不漏地分布在n*n的網格中。
空格移動的規則與八數碼游戲相同,實際上,八數碼就是一個n=3的奇數碼游戲。
現在給定兩個奇數碼游戲的局面,請判斷是否存在一種移動空格的方式,使得其中一個局面可以變化到另一個局面。
例如:
5 2 8
1 3 _
4 6 7
在游戲過程中,可以把空格與其上、下、左、右四個方向之一的數字交換(如果存在)。
例如在上例中,空格可與左、上、下面的數字交換,分別變成:
5 2 8 5 2 _ 5 2 8
1 _ 3 1 3 8 1 3 7
4 6 7 4 6 7 4 6 _
奇數碼游戲是它的一個擴展,在一個n*n的網格中進行,其中n為奇數,1個空格和1~n*n-1這n*n-1個數恰好不重不漏地分布在n*n的網格中。
空格移動的規則與八數碼游戲相同,實際上,八數碼就是一個n=3的奇數碼游戲。
現在給定兩個奇數碼游戲的局面,請判斷是否存在一種移動空格的方式,使得其中一個局面可以變化到另一個局面。
輸入
多組數據,對於每組數據:
第1行一個整數n,n<500,n為奇數。
接下來n行每行n個整數,表示第一個局面。
接下來n行每行n個整數,表示第二個局面。
局面中每個整數都是0~n*n-1之一,其中用0代表空格,其余數值與奇數碼游戲中的意義相同,保證這些整數的分布不重不漏。
第1行一個整數n,n<500,n為奇數。
接下來n行每行n個整數,表示第一個局面。
接下來n行每行n個整數,表示第二個局面。
局面中每個整數都是0~n*n-1之一,其中用0代表空格,其余數值與奇數碼游戲中的意義相同,保證這些整數的分布不重不漏。
輸出
對於每組數據,若兩個局面可達,輸出TAK,否則輸出NIE。
樣例輸入
3
1 2 3
0 4 6
7 5 8
1 2 3
4 5 6
7 8 0
1
0
0
樣例輸出
TAK
TAK
http://www.cnblogs.com/yuyixingkong/archive/2013/09/23/3335667.html
八數碼問題的有解無解的結論:
一個狀態表示成一維的形式,求出除0之外所有數字的逆序數之和,也就是每個數字前面比它大的數字的個數的和,稱為這個狀態的逆序。
若兩個狀態的逆序奇偶性相同,則可相互到達,否則不可相互到達。
N×N的棋盤,N為奇數時,與八數碼問題相同。
N為偶數時,空格每上下移動一次,奇偶性改變。稱空格位置所在的行到目標空格所在的行步數為空格的距離(不計左右距離),若兩個狀態的可相互到達,則有,兩個狀態的逆序奇偶性相同且空格距離為偶數,或者,逆序奇偶性不同且空格距離為奇數數。否則不能。

#include<bits/stdc++.h> #define N 252500 using namespace std; int a[255000],b[255000]; int n; void updata(int x[],int pos,int v) { while(pos<=N) { x[pos]+=v; pos+=pos&(-pos); } } int sum(int x[],int pos) { int ans=0; while(pos>0) { ans+=x[pos]; pos-=pos&(-pos); } return ans; } long long f(int x[]) { int c[255000]; long long ans=0; for(int i=1; i<=n; i++) { if(x[i]!=0) { ans+=sum(c,N)-sum(c,x[i]); updata(c,x[i],1); } } return ans; } int main() { while(scanf("%d",&n)==1) { n=n*n; for(int i=1; i<=n; i++) scanf("%d",&a[i]); for(int i=1; i<=n; i++) scanf("%d",&b[i]); if(f(a)%2==f(b)%2) printf("TAK\n"); else printf("NIE\n"); } return 0; }