题意:八数码,但是转移的方式是转动,一共十二种,有多组询问,初态唯一,终态不唯一。
题解:初态唯一,那么可以预处理出012345678的所有转移情况,然后将初态对012345678做一个映射,再枚举一下终态的所有情况,取最小值即可。
不得不学了逆cantor展开,cantor展开是一个变进制数,每位上是原序列对应位置上的逆序值。那么求逆时候,就先除最大的位权得到对应位置上的逆序值,根据逆序值可以知道他在原序列中第几大,然后标记它,迭代。状态转移有点烦。一开始还担心超时,就打了个表直接存到程序里,结果超过30000bit代码长度限制了,笑cry。
#include<cstdio> #include<cmath> #include<vector> #include<map> #include<set> #include<algorithm> #include<iostream> #include<cstring> #include<queue> using namespace std; typedef long long ll; //#define local const int maxn = 362880; int d[maxn]; int fac[9] = { 1,1,2,6,24,120,720,5040,40320};//,362880 int St[9],Ed[9]; int cantor(int *e) { int ret = 0; for(int i = 0; i < 9; i++) { int cnt = 0; for(int j = i+1; j < 9; j++) if(e[j] < e[i]) cnt++; ret += fac[8-i] * cnt; } return ret; } void invCantor(int *a,int code) { bool vis[10] = {0}; for(int i = 0; i < 9; i++){ int t = code/fac[8-i]; int j; for( j = 0; j < 9 ; j++){ if(!vis[j]){ if(t == 0) break; t--; } } a[i] = j; vis[j] = 1; code %= fac[8-i]; } } int dir[12][9] = { {2,0,1,3,4,5,6,7,8},{0,1,2,5,3,4,6,7,8},{0,1,2,3,4,5,8,6,7}, {1,2,0,3,4,5,6,7,8},{0,1,2,4,5,3,6,7,8},{0,1,2,3,4,5,7,8,6}, {6,1,2,0,4,5,3,7,8},{0,7,2,3,1,5,6,4,8},{0,1,8,3,4,2,6,7,5}, {3,1,2,6,4,5,0,7,8},{0,4,2,3,7,5,6,1,8},{0,1,5,3,4,8,6,7,2} }; queue<int> q; void BfsPre() { memset(d,-1,sizeof(d) ); d[0] = 0; q.push(0); while(q.size()){ int u = q.front();q.pop(); int tmp[9]; invCantor(tmp,u); for(int i = 0; i < 12; i++){ int tmp2[9]; for(int j = 0; j < 9; j++){ tmp2[j] = tmp[dir[i][j]]; } int v = cantor(tmp2); if(~d[v]) continue; d[v] = d[u]+1; q.push(v); } } } int query() { int mp[9]; for(int i = 0; i < 9; i++) { mp[St[i]] = i; } bool appear[9] = {0}; for(int i = 0; i < 9; i++){ if(~Ed[i]) appear[Ed[i] = mp[Ed[i]]] = 1; } int vec[9],sz = 0; for(int i = 0; i < 9; i++) { if(!appear[i]) vec[sz++] = i; } if(sz == 9) return 0; if(sz == 0) return d[cantor(Ed)]; const int INF = 0x7fffffff; int ans = INF; int tmp[9]; do{ int j = 0; for(int i = 0; i < 9; i++){ if(~Ed[i]) { tmp[i] = Ed[i]; } else { tmp[i] = vec[j++]; } } int Hash = cantor(tmp); if(~d[Hash]) ans = min(ans,d[Hash]); }while(next_permutation(vec,vec+sz)); return ans!=INF? ans : -1; } int main() { #ifdef local freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); #endif // local int T; BfsPre(); scanf("%d",&T); for(int k = 1; k <= T; k++){ printf("Case #%d: ",k); for(int i = 0; i < 9; i ++){ scanf("%d",St+i); St[i]--; } getchar(); char buf[15]; for(int i = 0; i < 3; i++){ gets(buf); for(int j = 0; j < 5; j+=2){ if('1'<=buf[j] && buf[j] <= '9'){ Ed[i*3+j/2] = buf[j] - '1'; }else Ed[i*3+j/2] = -1; } } int ans = query(); if(~ans) printf("%d\n",ans); else printf("No Solution!\n"); } return 0; }