轉載請註明出處,謝謝http://blog.csdn.net/acm_cxlove/article/details/7854526
by---cxlove
題目:給出24個格子的數位的初始態和目標態,然後問能不能轉換
http://acm.hdu.edu.cn/showproblem.php?pid=4021
思路:看起來很像著名的“八數位”問題,首先,針對八個特殊位置(死角),如果這裡有空位就把它和相鄰的位置交換,這樣之後如果兩個狀態的對應死角上的數字不同,那麼顯然是不能達到指定狀態的,因為無法把死角處的數字換出去。
搞定了死角後就只剩下4×4的board了,這就變成了八數位問題的拓展——15數位。首先想想八數位是如何判斷有解的:首先把所有數字(不包括空位的0)寫成一行,就得到了一個1~8的排列,考慮空位的交換情況:1.左右交換,2.上下交換。對於左右交換而言,是不會改變寫出的排列的逆序數的;而對上下交換,相當於在排列中向前或向後跳了兩個位置,那麼要麼兩個數都比它大或小,這樣逆序數加2或減2,要麼兩個數一個比它大一個比它小,這樣逆序數不變,綜上,對於八數位問題,操作不會改變逆序數的奇偶性,所以只有初始狀態和指定狀態的逆序數奇偶性相同就有解。
弄清楚了八數位,擴充起來就容易了,現在我們將其擴充到N維(即N*N的board,N*N-1數位問題)。
首先無論N的奇偶,左右交換不改變逆序數,N為奇數時:上下交換逆序數增加N-1或減少N-1或不變,因為N為奇數,所以逆序數奇偶性不變;而N為偶數時:上下交換一次奇偶性改變一次。
結論:N為奇數時,初始狀態與指定狀態逆序數奇偶性相同即有解;N為偶數時,先計算出從初始狀態到指定狀態,空位要移動的行數m,如果初始狀態的逆序數加上m與指定狀態的逆序數奇偶性相同,則有解。
好了,現在這道題就簡單了,計算逆序數和空格要移動的行數即可。
#include<iostream>#include<cstdio>#include<map>#include<cstring>#include<cmath>#include<vector>#include<queue>#include<algorithm>#include<set>#define inf 110000000#define M 10005#define N 10005#define Min(a,b) ((a)<(b)?(a):(b))#define Max(a,b) ((a)>(b)?(a):(b))#define pb(a) push_back(a)#define mem(a,b) memset(a,b,sizeof(a))#define LL long long#define MOD 1000000007using namespace std;int init[24],goal[24];int a[8]={0,1,2,7,16,21,22,23};int b[16]={3,4,5,6,8,9,10,11,12,13,14,15,17,18,19,20};int gao(int b){ if(b==0||b==2) return 3; if(b==1||b==7) return 6; if(b==16||b==22) return 17; else return 20;}int main(){ int t; scanf("%d",&t); while(t--){ for(int i=0;i<24;i++) scanf("%d",&init[i]); for(int i=0;i<8;i++) if(init[a[i]]==0) swap(init[a[i]],init[gao(a[i])]); for(int i=0;i<24;i++) scanf("%d",&goal[i]); for(int i=0;i<8;i++) if(goal[a[i]]==0) swap(goal[a[i]],goal[gao(a[i])]); bool ans=true; for(int i=0;i<8&&ans;i++){ // cout<<init[a[i]]<<" "<<goal[a[i]]<<endl; if(init[a[i]]!=goal[a[i]]) ans=false; } if(!ans){puts("Y");continue;} int cnt1=0,cnt2=0,pos1=0,pos2=0; for(int i=1;i<16;i++){ if(init[b[i]]==0){pos1=i;continue;} for(int j=0;j<i;j++) if(init[b[j]]>init[b[i]]) cnt1++; } for(int i=1;i<16;i++){ if(goal[b[i]]==0){pos2=i;continue;} for(int j=0;j<i;j++) if(goal[b[j]]>goal[b[i]]) cnt2++; } if((abs(pos1/4-pos2/4)+cnt1-cnt2)%2==0) puts("N"); else puts("Y"); } return 0;}