[转载]双向广搜
?
?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;
}
轉載于:https://www.cnblogs.com/csgc0131123/p/5290397.html
總結
- 上一篇: 第0周学习资源阅读感悟
- 下一篇: [Elasticsearch] 部分匹配