1. 程式人生 > >[USACO3.2]Magic Squares 魔板

[USACO3.2]Magic Squares 魔板

題目大意

給出一個2*4的矩陣,有3種基本操作

A B C
交換上下兩行 將最右邊的一列插入最左邊 中央四格作順時針旋轉

基本狀態:
1 2 3 4
8 7 6 5
給定一個目標狀態,求最少的基本操作完成基本狀態到目標狀態的轉換,輸出基本操作序列。

題目解析

該題目很明顯是用搜索做,如果用深搜,則會超時,所以只能用寬搜。
但是如果沒有一種好的方法來判斷是否已經加入列表,可能會超時,所以,雜湊表。

程式碼

#include<iostream>
#include<string>
#define p 100003//質數 
using namespace std;
int x,f[p],num[p],head,tail=1;//f標記父節點   num表示當前狀態的最少運算元 
int r[3][8]={{8,7,6,5,4,3,2,1},{4,1,2,3,6,7,8,5},{1,7,2,4,5,3,6,8}};//變換的規則 
char ans[p];//儲存 A B C 
string g,a[p],state[p];//g為目標狀態 
bool hash(string s)//雜湊函式
{
	int k=0,i=0;
	for(int i=0;i<8;i++)
	 k=k*10+s[i]-48;
	k%=p;
	while(a[(k+i)%p]!=""&&a[(k+i)%p]!=s)
	 i++;
	if(a[(i+k)%p]=="")
	{
	  a[(i+k)%p]=s;
	  return false;
	}
	return true;
} 
void bfs()
{
	hash("12345678");
	state[1]="12345678";//初始化 
	do
	{
	  head++;
	  for(int i=0;i<3;i++)
	  {
	  	tail++;
	  	f[tail]=head;
	  	state[tail]="";
	  	num[tail]=num[head]+1;
	  	ans[tail]='A'+i;
	  	for(int j=0;j<8;j++)
	  	 state[tail]+=state[head][r[i][j]-1];//字串從0為開始算 
	  	if(hash(state[tail])) tail--;//判斷是否能夠入隊 
	  	else if(state[tail]==g) return;//找到目標狀態 
	  }
	}while(head<tail);
}
void out(int x)//輸出 
{
	if(x==1) return;
	out(f[x]);
	cout<<ans[x];
}
int main()
{
	for(int i=1;i<=8;i++)
	{
	  cin>>x;
	  g+=x+48;
	}//目標狀態轉換為字串 
	if(g=="12345678") 
	{
	  cout<<0;
	  return 0;
	}//特判 
	bfs();//寬搜 
	cout<<num[tail]<<endl;
	out(tail);
}