1. 程式人生 > >HDU 1430 魔板

HDU 1430 魔板

ati 處理 目標 同時 ble -- 操作方法 void 右移

在魔方風靡全球之後不久,Rubik先生發明了它的簡化版——魔板。魔板由8個同樣大小的方塊組成,每個方塊顏色均不相同,可用數字1-8分別表示。任一時刻魔板的狀態可用方塊的顏色序列表示:從魔板的左上角開始,按順時針方向依次寫下各方塊的顏色代號,所得到的數字序列即可表示此時魔板的狀態。例如,序列(1,2,3,4,5,6,7,8)表示魔板狀態為:


1 2 3 4
8 7 6 5

對於魔板,可施加三種不同的操作,具體操作方法如下:

A: 上下兩行互換,如上圖可變換為狀態87654321
B: 每行同時循環右移一格,如上圖可變換為41236785
C: 中間4個方塊順時針旋轉一格,如上圖可變換為17245368


給你魔板的初始狀態與目標狀態,請給出由初態到目態變換數最少的變換步驟,若有多種變換方案則取字典序最小的那種。

hdu 鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=1430

BFS + 康拓展開 + 打表 + 映射

#include <bits/stdc++.h> 
const int N = 8; 
const int MAX = 40323;
using namespace std;

/*
魔板 hdu 1430 搜索 + 康拓展開。
 http://acm.hdu.edu.cn/showproblem.php?pid=1430
BFS + 打表預處理 + 康拓展開 https://www.cnblogs.com/H-Vking/p/4346004.html 關於打表預處理:   由於魔板的所有狀態都可以轉換為“12345678”, 所以這時就需要做一個映射:每組數據都有一個起始狀態與目標狀態, 可以把起始狀態用一種映射關系映射為“12345678”, 然後用這種映射關系再去改一下終止狀態。例如:初態為“12653487” , 目態為“12345678” ;這時映射後初態為“12345678”, 即f[1] = 1 , f[2] = 2 , f[6] = 3 , f[5] = 4 , f[3] = 5 , f[4] = 6 , f[8] = 7 , f[7] = 8 ,按照這種 映射關系目態應為“12564387”。 代碼應為:f[start[i] - ‘0‘] = i ; end[i] = f[end[i] - ‘0‘] + ‘0‘;  有這樣一個映射前提, 可以先用BFS預處理從“12345678”到其余所有狀態的步驟, 然後輸入每組測試數據後進行轉換,然後這時候就變成了 求從“12345678”到映射後的目標狀態的步驟的問題, 這時按照存好的路徑輸出即可。
*/ struct no { int num;//上一狀態的編號 int c; no(){ num = -1;c =-1; } }dis[MAX]; typedef struct { int num;//對應kt編號 string st; }node; int fac[] = {1,1,2,6,24,120,720,5040,40320}; string str;//初始狀態 string Tstr;//目標狀態 int Tflag ; int q; queue<node> qu; //康拓的逆(這題不需要) void ktn(char a[9],int k) { k--;//這別忘了,第12個,一定從11開始計算 int vis[10]={0};int j = 0; for (int i=0;i<N;++i) { int t = k/fac[N-i-1]; for ( j=1;j<=N;++j) //計算出它到底是幾,這裏要排除出現過的數 if (!vis[j]) { if (!t) break; t--; } a[i]=j+0; vis[j] =1; k %= fac[N-i-1]; } } //康拓展開 int kt(string &arr) { int ans = 0 ; for (int i=0;i<N;++i) { int t = 0; //記錄後面比它小的數字個數 for (int j = i+1;j<N;++j) { if (arr[j] < arr[i]) t++; } ans += t*fac[N-i-1]; } return ans; } string change (string t,int s) { string a(t); if (s == 1) { for(int i=N-1;i>=0;--i) a[N-i-1] = t[i]; } else if (s == 2) { a[0] = t[3]; a[7] = t[4]; int v = 1; for (int i=0;i<N;++i) { if (i==3 || i == 4) continue; a[v++] = t[i]; } } else { char s1 = t[1];char s2 = t[2]; char s5 = t[5];char s6 = t[6]; a[2] = s1; a[5] = s2;a[6] = s5; a[1]=s6; } return a; } void bfs() { node temp ; string s; int v; while(!qu.empty()) { temp = qu.front();qu.pop(); for (int i=1;i<=3;++i) // A,B,C { s = change(temp.st,i); v =kt(s); if(dis[v].num == -1) { node t ;t.st = s;t.num = v; qu.push(t); dis[v].num = temp.num;dis[v].c =i; } } } } void putans() { char ans[MAX]; int i = 0; int j = Tflag; while(j!= q) { ans[i++] = dis[j].c+A-1; j = dis[j].num; } for (int j = i-1;j>=0;--j) printf ("%c",ans[j]); printf ("\n"); } int main () { str = "12345678"; char s1[10]; q = kt(str); node t ;t.st = str;t.num = q; qu.push(t); bfs(); //打表預處理,從12345678 到其他態的路徑算出 while(cin>>str>>Tstr) { //利用一種映射關系 將 起始映射為12345678 對應的終態也就映射成另一個了 //然後就可以查詢了(比如 23456781 -- 12345678的映射,就是,2對應1,3對應2...) //映射 for (int i=0;i<N;++i) s1[str[i]-0] = i+1; //這裏,s1 數組就相當於一個函數了,來映射 for (int i=0;i<N;++i) Tstr[i] = s1[Tstr[i]-0] +0; Tflag = kt(Tstr); putans(); } return 0; }/* 63728145 86372541 */

HDU 1430 魔板