双向广搜 8数码问题
生活随笔
收集整理的這篇文章主要介紹了
双向广搜 8数码问题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?轉載自:http://blog.sina.com.cn/s/blog_8627bf080100ticx.html?
?Eight ??
? ?題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=1043? ?講到雙向廣搜,那就不能不講經典的八數碼問題,有人說不做此題人生不完整 。
? ?所謂雙向廣搜,就是初始結點向目標結點和目標結點向初始結點同時擴展,直至在兩個擴展方向上出現同一個結點,搜索結束。它適用的問題是,擴展結點較多,而目標結點又處在深沉,如果采用單純的廣搜解題,搜索量巨大,搜索速度慢是可想而知的,同時往往也會出現內存空間不夠用的情況,這時雙向廣搜的作用就體現出來了。雙向廣搜對單純的廣搜進行了改良或改造,加入了一定的“智能因數”,使搜索能盡快接近目標結點,減少了在空間和時間上的復雜度。
? ?當在講題前,不得不先給大家補充一點小知識,大家都知道搜索的題目其中難的一部分就是事物的狀態,不僅多而且復雜,要怎么保存每時刻的狀態,又容易進行狀態判重呢,這里提到了一種好辦法 ? ------康托展開(只能針對部分問題)
?
康托展開
康托展開式:
? X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
?其中,a為整數,并且0<=ai<i(1<=i<=n)。
?
例:
問:1324是{1,2,3,4}排列數中第幾個大的數?
解:第一位是1小于1的數沒有,是0個 0*3! 第二位是3小于3的數有1和2,但1已經在第一位了,所以只有一個數2 1*2! 。第三位是2小于2的數是1,但1在第一位,所以有0個數 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2個,1324是第三個大數。
?
好吧,先看下代碼實現:
int factory[]={1,1,2,6,24,120,720,5040,40320,362880}; // 0..n的階乘
?
int Gethash(char eight[])
{
? ? ? ?int k=0;
? ? ? ?for(int i=0;i<9;i++) ? ?// 因為它有八位數(針對八數碼問題)
? ? ? ?{
? ? ? ? ? ? ? int t=0;
? ? ? ? ? ? ? for(int j=i+1;j<9;j++)
? ? ? ? ? ? ? ? ? ? ?if(eight[j]<eight[i])
? ? ? ? ? ? ? ? ? ? ? ? ? ? t++;
? ? ? ? ? ? ? k+=(t*factory[9-i-1]);
? ? ? ?}
? ? ? ?return k; ? // 返回該數是第幾大
}
好的,現在再來看看雙向廣搜模版:
void TBFS()
{
? ? ? ?bool found=false;
? ? ? ?memset(visited,0,sizeof(visited)); ?// 判重數組
? ? ? ?while(!Q1.empty()) ?Q1.pop(); ? // 正向隊列
? ? ? ?while(!Q2.empty()) ?Q2.pop(); ?// 反向隊列
? ? ? ?//======正向擴展的狀態標記為1,反向擴展標記為2
? ? ? ?visited[s1.state]=1; ? // 初始狀態標記為1
? ? ? ?visited[s2.state]=2; ? // 結束狀態標記為2
? ? ? ?Q1.push(s1); ?// 初始狀態入正向隊列
? ? ? ?Q2.push(s2); ?// 結束狀態入反向隊列
? ? ? ?while(!Q1.empty() || !Q2.empty())
? ? ? ?{
? ? ? ? ? ? ? if(!Q1.empty())
? ? ? ? ? ? ? ? ? ? ?BFS_expand(Q1,true); ?// 在正向隊列中搜索
? ? ? ? ? ? ? if(found) ?// 搜索結束?
? ? ? ? ? ? ? ? ? ? ?return ;
? ? ? ? ? ? ? if(!Q2.empty())
? ? ? ? ? ? ? ? ? ? ?BFS_expand(Q2,false); ?// 在反向隊列中搜索
? ? ? ? ? ? ? if(found) // 搜索結束
? ? ? ? ? ? ? ? ? ? ?return ;
? ? ? ?}
}
void BFS_expand(queue<Status> &Q,bool flag)
{ ?
? ? ? ?s=Q.front(); ?// 從隊列中得到頭結點s
? ? ? Q.pop()
? ? ? for( 每個s 的子節點 t )
? ? ?{
? ? ? ? ? ? ?t.state=Gethash(t.temp) ?// 獲取子節點的狀態
? ? ? ? ? ? ?if(flag) ? // 在正向隊列中判斷
? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? ? if (visited[t.state]!=1)// 沒在正向隊列出現過
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ?if(visited[t.state]==2) ?// 該狀態在反向隊列中出現過
? ? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?各種操作;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?found=true;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?return;
? ? ? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ? ? visited[t.state]=1; ? // 標記為在在正向隊列中
? ? ? ? ? ? ? ? ? ? ? ? ? ? Q.push(t); ?// 入隊
? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ?}
? ? ? ? ? ? ?else ? ?// 在正向隊列中判斷
? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? ? if (visited[t.state]!=2) // 沒在反向隊列出現過
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ?if(visited[t.state]==1) ?// 該狀態在正向向隊列中出現過
? ? ? ? ? ? ? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 各種操作;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? found=true;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?visited[t.state]=2; ?// 標記為在反向隊列中
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Q.push(t); ?// 入隊
? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ?} ? ? ? ? ? ??
} ? ? ? ? ? ? ? ? ? ??
?
好的,現在開始說說八數碼問題
其實,Eight有一個很重要的判斷,那就是逆序數的判斷。如果i>j,并且ai<aj,那么定義(i,j)為一個逆序對,而對于一個狀態排列中所含的逆序對的個數總和就是逆序數。而本題的逆序數的奇偶性的判斷是至關重要的:
如果x在同一行上面移動那么1~8的逆序數不變
如果x在同一列上面移動,每次逆序數增加偶數個或者減少偶數個
因為目標結點的狀態的逆序數為0,為偶數,所以每次訪問到的狀態的逆序數也必須為偶數,保持奇偶性性質,否則就不必保存該狀態。
?
#include<iostream>
#include<queue>
using namespace std;
?
#define N 10
#define MAX 365000
?
char visited[MAX];
int father1[MAX]; ?// 保存正向搜索當前狀態的父親狀態結點
int father2[MAX]; ?// 保存反向搜索當前狀態的父親狀態結點
int move1[MAX]; ? ?// 正向搜索的方向保存
int move2[MAX]; ? // ?反向搜索的方向保存
?
struct Status ? // 結構
{
? ? ? ?char eight[N]; ?// 八數碼狀態
? ? ? ?int space; ? ? // x 位置
? ? ? ?int state; ? ?// hash值,用于狀態保存與判重?
};
?
queue<Status> Q1; ?// 正向隊列
queue<Status> Q2; ?// 反向隊列
?
Status s,s1,s2,t;
?
bool found; ?// 搜索成功標記
?
int state; ? // 正反搜索的相交狀態
?
int factory[]={1,1,2,6,24,120,720,5040,40320,362880}; ?// 0..n的階乘
?
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
?
int Gethash(char eight[]) ?// 康托展開(獲取狀態,用于判重)
{
? ? ? ?int k=0;
? ? ? ?for(int i=0;i<9;i++)
? ? ? ?{
? ? ? ? ? ? ? int t=0;
? ? ? ? ? ? ? for(int j=i+1;j<9;j++)
? ? ? ? ? ? ? ? ? ? ?if(eight[j]<eight[i])
? ? ? ? ? ? ? ? ? ? ? ? ? ? t++;
? ? ? ? ? ? ? k+=(t*factory[9-i-1]);
? ? ? ?}
? ? ? ?return k;
}
?
int ReverseOrder(char eight[]) ?// 求狀態的逆序數
{
? ? ? ?int i,j,num=0;
? ? ? ?for(i=0;i<9;i++)
? ? ? ?{
? ? ? ? ? ? ? for(j=0;j<i;j++)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?if(int(eight[i])==9)
? ? ? ? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ?if(int(eight[j])==9)
? ? ? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? ? ? ?if(int(eight[j])>int(eight[i]))
? ? ? ? ? ? ? ? ? ? ? ? ? ? num++;
? ? ? ? ? ? ? }
? ? ? ?}
? ? ? ?num=num%2;
? ? ? ?return num;
}
?
void BFS_expand(queue<Status> &Q,bool flag) ?// 單向廣度搜索
{
? ? ? ?int k,x,y;
? ? ? ?s=Q.front();
? ? ? ?Q.pop();
? ? ? ?k=s.space;
? ? ? ?x=k/3;
? ? ? ?y=k%3;
? ? ? ?for(int i=0;i<4;i++)
? ? ? ?{
? ? ? ? ? ? ? int xx=x+dir[i][0];
? ? ? ? ? ? ? int yy=y+dir[i][1];
? ? ? ? ? ? ? if(xx>=0 && xx<=2 && yy>=0 && yy<=2)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?t=s;
? ? ? ? ? ? ? ? ? ? ?t.space=xx*3+yy; ? // 計算x位置
? ? ? ? ? ? ? ? ? ? ?swap(t.eight[k],t.eight[t.space]); ?// 交換兩個數位置
? ? ? ? ? ? ? ? ? ? ?t.state=Gethash(t.eight);
? ? ? ? ? ? ? ? ? ? ?if(flag) ?// 在正向隊列中判斷
? ? ? ? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? ? ? ? ? if(visited[t.state]!=1 && ReverseOrder(t.eight)==0) ?// 未在正向隊列出現過并且滿足奇偶性
? ? ? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?move1[t.state]=i; ?// 保存正向搜索的方向
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?father1[t.state]=s.state; // 保存正向搜索當前狀態的父親狀態結點
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if(visited[t.state]==2) ? // ?當前狀態在反向隊列中出現過
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? state=t.state; ?// 保存正反搜索中相撞的狀態(及相交點)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? found=true; ? ?// 搜索成功
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?visited[t.state]=1; ? // 標記為在正向隊列中
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Q.push(t); ?// 入隊
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ?else ?// 在反向隊列中判斷
? ? ? ? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? ? ? ? ? if(visited[t.state]!=2 && ReverseOrder(t.eight)==0) ? // 未在反向隊列出現過并且滿足奇偶性
? ? ? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?move2[t.state]=i; ?// 保存反向搜索的方向
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?father2[t.state]=s.state; // 保存反向搜索當前狀態的父親狀態結點
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if(visited[t.state]==1) ?// ?當前狀態在正向隊列中出現過
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? state=t.state; ?// 保存正反搜索中相撞的狀態(及相交點)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? found=true; ? // 搜索成功
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?visited[t.state]=2; ?// 標記為在反向隊列中
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Q.push(t); ? // 入隊
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? }
? ? ? ?}
? ? ? ?return ;
}
?
void TBFS() ? ? ? ? ? ?// 雙向搜索
{
? ? ? ?memset(visited,0,sizeof(visited));
? ? ? ?while(!Q1.empty())
? ? ? ? ? ? ? Q1.pop();
? ? ? ?while(!Q2.empty())
? ? ? ? ? ? ? Q2.pop();
? ? ? ?visited[s1.state]=1; ? // 初始狀態
? ? ? ?father1[s1.state]=-1;
? ? ? ?visited[s2.state]=2; ? // 目標狀態
? ? ? ?father2[s2.state]=-1;
? ? ? ?Q1.push(s1);
? ? ? ?Q2.push(s2);
? ? ? ?while(!Q1.empty() || !Q2.empty())
? ? ? ?{
? ? ? ? ? ? ? if(!Q1.empty())
? ? ? ? ? ? ? ? ? ? ?BFS_expand(Q1,true);
? ? ? ? ? ? ? if(found)
? ? ? ? ? ? ? ? ? ? ?return ;
? ? ? ? ? ? ? if(!Q2.empty())
? ? ? ? ? ? ? ? ? ? ?BFS_expand(Q2,false);
? ? ? ? ? ? ? if(found)
? ? ? ? ? ? ? ? ? ? ?return ;
? ? ? ?}
}
?
void PrintPath1(int father[],int move[]) ? // 從相交狀態向初始狀態尋找路徑
{
? ? ? ?int n,u;
? ? ? ?char path[1000];
? ? ? ?n=1;
? ? ? ?path[0]=move[state];
? ? ? ?u=father[state];
? ? ? ?while(father[u]!=-1)
? ? ? ?{
? ? ? ? ? ? ? path[n]=move[u];
? ? ? ? ? ? ? n++;
? ? ? ? ? ? ? u=father[u];
? ? ? ?}
? ? ? ?for(int i=n-1;i>=0;--i)
? ? ? ?{ ? ? ??
? ? ? ? ? ? ? if(path[i] == 0) ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ?printf("u"); ? ? ??
? ? ? ? ? ? ? else if(path[i] == 1) ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ?printf("d"); ? ? ??
? ? ? ? ? ? ? else if(path[i] == 2) ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ?printf("l"); ? ? ??
? ? ? ? ? ? ? else ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ?printf("r"); ??
? ? ? ?}
}
?
void PrintPath2(int father[],int move[]) ? // 從相交狀態向目標狀態尋找路徑
{
? ? ? ?int n,u;
? ? ? ?char path[1000];
? ? ? ?n=1;
? ? ? ?path[0]=move[state];
? ? ? ?u=father[state];
? ? ? ?while(father[u]!=-1)
? ? ? ?{
? ? ? ? ? ? ? path[n]=move[u];
? ? ? ? ? ? ? n++;
? ? ? ? ? ? ? u=father[u];
? ? ? ?}
? ? ? ?for(int i=0;i<=n-1;i++)
? ? ? ?{ ? ? ??
? ? ? ? ? ? ? if(path[i] == 0) ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ?printf("d"); ? ? ??
? ? ? ? ? ? ? else if(path[i] == 1) ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ?printf("u"); ? ? ??
? ? ? ? ? ? ? else if(path[i] == 2) ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ?printf("r"); ? ? ??
? ? ? ? ? ? ? else ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ?printf("l"); ??
? ? ? ?}
}
?
int main()
{
? ? ? ?int i;
? ? ? ?char c; ??
? ? ? ?while(scanf(" %c",&c)!=EOF)
? ? ? ?{
? ? ? ? ? ? ? if(c=='x')
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?s1.eight[0]=9;
? ? ? ? ? ? ? ? ? ? ?s1.space=0;
? ? ? ? ? ? ? }
? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ?s1.eight[0]=c-'0';
? ? ? ? ? ? ? for(i=1;i<9;i++)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?scanf(" %c",&c);
? ? ? ? ? ? ? ? ? ? ?if(c=='x')
? ? ? ? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? ? ? ? ? s1.eight[i]=9;
? ? ? ? ? ? ? ? ? ? ? ? ? ? s1.space=i;
? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ?else
? ? ? ? ? ? ? ? ? ? ? ? ? ? s1.eight[i]=c-'0';
? ? ? ? ? ? ? }
? ? ? ? ? ? ? s1.state=Gethash(s1.eight);
? ? ? ? ? ? ? for(int i=0;i<9;i++)
? ? ? ? ? ? ? ? ? ? ?s2.eight[i]=i+1;
? ? ? ? ? ? ? s2.space=8;
? ? ? ? ? ? ? s2.state=Gethash(s2.eight);
? ? ? ? ? ? ? if(ReverseOrder(s1.eight)==1)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?cout<<"unsolvable"<<endl;
? ? ? ? ? ? ? ? ? ? ?continue;
? ? ? ? ? ? ? }
? ? ? ? ? ? ? found=false;
? ? ? ? ? ? ? TBFS();
? ? ? ? ? ? ? if(found) ? // 搜索成功
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?PrintPath1(father1,move1);
? ? ? ? ? ? ? ? ? ? ?PrintPath2(father2,move2);
? ? ? ? ? ? ? }
? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ?cout<<"unsolvable"<<endl;
? ? ? ? ? ? ? cout<<endl;
? ? ? ?}
? ? ? ?return 0;
}
總結
以上是生活随笔為你收集整理的双向广搜 8数码问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 消息称三星 Galaxy S23 手机国
- 下一篇: 搜索训练1 [8数码问题]