A*搜索入门,这个文章讲解的很清晰了,Here
hiho里面讲解的也很清楚
经典的八数码问题,在搜索的时候把棋盘当前的状态,看做一个1-9的排列(我把0换成了9),但是要对于每一个排列进行排重, 有一个比较好的方法是 康拓展开,及其逆展开,可以计算一个排列在形成的所有排列中的名次,以及给出一个名次,还原出这个排列。剩下的就是A*搜索
主要是bfs,然后加上剪枝,即F函数
G:表示出起点到当前点实际的距离
H:当前点到目标点的估计值(不大于实际值才可以)
F:H+G,确定每个点的搜索的优先级
A*真的很快,跑了200+ms
直接bfs的跑了5000+ms
A*的
#include<bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define fastIO ios::sync_with_stdio(false);cin.tie(0);
#define LL long long
#define pb push_back
#define gcd __gcd
#define For(i,j,k) for(int i=(j);i<=k;i++)
#define lowbit(i) (i&(-i))
#define _(x) printf("%d\n",x)
typedef vector<LL> vec;
typedef pair<int,int> PI;
const double EPS = 1e-8;
const int maxn = 5e6;
const int inf = 1 << 28;
int fac[10];
void init(){
fac[0] = fac[1] = 1;
for(int i=2;i<=9;i++)fac[i] = fac[i-1] * i;
}
int n = 9;
int arrayToInt(int a[]){ //编码一个排列
int ans=0,i,j,tmp;
for(i=1;i<=n;i++){
tmp = 0;
for(j=i+1;j<=n;j++)if(a[j]<a[i])tmp++;
ans+=fac[n-i]*tmp;
}
return ans;
}
int fun(int a[3][3]){ //二维数组展开一维,再调用康拓展开
int b[10],k=1;
for(int i=0;i<3;i++)for(int j=0;j<3;j++){
b[k++] = a[i][j];
}
return arrayToInt(b);
}
int getH(int a[3][3]){ //计算估价函数,这里用当前状态到目标状态的曼哈顿距离
int ans=0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
ans += abs(i - (a[i][j] - 1) / 3) + abs(j - (a[i][j] - 1) % 3);
}
}
return ans;
}
int dir[4][2]={0,-1,0,1,-1,0,1,0};
struct node{
int a[3][3];
int x,y; //9的位置
int F; //启发函数F (F = G + H);
int G,H; //G:起点到当前点的步数,H:目标点到当前点期望的步数
bool operator<(const node&rhs) const{
return F > rhs.F || F == rhs.F && G > rhs.G;
}
}st;
bool vis[maxn];
int bfs(){
st.G = 0;
st.H = getH(st.a);
st.F = st.G + st.H;
priority_queue<node> q;//相当于文中openlist,vis数组相当于closedlist
cl(vis,false);
q.push(st);
if(fun(st.a)==0)return 0;
while(!q.empty()){
node tmp = q.top();q.pop();
for(int i=0;i<4;i++){
node cur = tmp;
cur.x += dir[i][0];
cur.y += dir[i][1];
if(cur.x<0||cur.y<0||cur.x>2||cur.y>2)continue;
swap(cur.a[cur.x][cur.y], cur.a[tmp.x][tmp.y]);
int val = fun(cur.a);
if(!vis[val]){
/*表示不在closedlist中,要么在openlist中,
要么不在openlist,也就是第一次访问,都进行更新。
如果已经在openlist中,为什么还要更新?
因为某一个点的G可能会在后来被更新为一个较小的值,
这样他的F也会变小,所以还是要加入,这里我没有直接找到那个点进行修改,
而是直接在加入一个这样的新点,由排序的条件,使得他排在前面*/
cur.G = tmp.G + 1;
cur.H = getH(cur.a);
cur.F = cur.G + cur.H;
q.push(cur);
}
if(val == 0) return cur.G;
}
vis[fun(tmp.a)] = true;//遍历完当前点可达的所有点,那么当前点就可以加入closedlist表
}
return -1;
}
int a[3][3];
int main(){
init();
int T;scanf("%d",&T);
while(T--){
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
scanf("%d",&st.a[i][j]);
if(st.a[i][j]==0){
st.a[i][j] = 9;st.x = i;st.y = j;
}
}
}
int ans = bfs();
if(ans == -1)
puts("No Solution!");
else
printf("%d\n",ans);
}
return 0;
}
/*
10
5 8 0
2 7 4
3 1 6
3 4 2
5 7 0
6 8 1
26 25
*/
直接bfs的
#include<bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define fastIO ios::sync_with_stdio(false);cin.tie(0);
#define LL long long
#define pb push_back
#define gcd __gcd
#define For(i,j,k) for(int i=(j);i<=k;i++)
#define lowbit(i) (i&(-i))
#define _(x) printf("%d\n",x)
typedef vector<LL> vec;
typedef pair<int,int> PI;
const double EPS = 1e-8;
const int maxn = 4e6;
const int inf = 1 << 28;
int fac[10];
void init(){
fac[0] = fac[1] = 1;
for(int i=2;i<=9;i++)fac[i] = fac[i-1] * i;//cout<<fac[i]<<endl;
}
/*
O(n*n)
全排列散列,康拓展开,康拓逆展开
n数据范围,a[] 排列数组,下标1开始
ans:排名,0开始
*/
int n = 9;
void intToArray(int x,int a[]){
bool used[10];cl(used,false);
int i,j,tmp;
for(i=1;i<=n;i++){
tmp = x / fac[n-i];
for(j=1;j<=n;j++)if(!used[j]){
if(tmp==0)break;tmp--;
}
a[i] = j;
used[j] = true;
x %= fac[n-i];
}
}
int arrayToInt(int a[]){
int ans=0,i,j,tmp;
for(i=1;i<=n;i++){
tmp = a[i] - 1;
for(j=1;j<i;j++)if(a[j]<a[i])--tmp;
ans+=fac[n-i]*tmp;
}
return ans;
}
//--end
int fun(int a[3][3]){
int b[10],k=1;
for(int i=0;i<3;i++)for(int j=0;j<3;j++){
b[k++] = a[i][j];
}
return arrayToInt(b);
}
int step[maxn];
int dir[][4]={{0,0,1,-1},{-1,1,0,0}};
int bfs(int st){
queue<int> q;
cl(step,-1);
q.push(st);
step[st] = 0;
while(!q.empty()){
//
int cur = q.front();q.pop();
if(cur == 0) return step[cur];
int tmp[10], tmp2[10];
intToArray(cur,tmp);
int Rank ;
for(int i=1;i<=9;i++)if(tmp[i]==9){Rank = i - 1;break;}
int x = Rank / 3;
int y = Rank % 3;
for(int i=0;i<4;i++){
int dx = x + dir[0][i];
int dy = y + dir[1][i];
if(dx < 0 || dy < 0 || dx >= 3 || dy >= 3)continue;
//printf("dx = %d, dy = %d\n",dx,dy);
int t = dx * 3 + dy + 1;
memcpy(tmp2,tmp,sizeof(tmp));
//printf("==================%d %d\n",tmp2[Rank+1],tmp2[t]);
swap(tmp2[Rank+1],tmp2[t]);
int X = arrayToInt(tmp2);
if(step[X]!=-1)continue;
step[X] = step[cur] + 1;
q.push(X);
}
}
return -1;
}
int a[3][3];
int main(){
int T;init();
scanf("%d",&T);
while(T--){
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
scanf("%d",&a[i][j]);
if(a[i][j]==0)a[i][j] = 9;
}
}
int ans = bfs(fun(a));
if(ans == -1){
puts("No Solution!");
}
else printf("%d\n",ans);
}
return 0;
}
如果写错了,欢迎打骂,批评指正