关闭

HIHO #1312 : 搜索三·启发式搜索

231人阅读 评论(0) 收藏 举报
分类:

题目链接

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;
}

如果写错了,欢迎打骂,批评指正

0
0
查看评论

1312 : 搜索三·启发式搜索

时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 在小Ho的手机上有一款叫做八数码的游戏,小Ho在坐车或者等人的时候经常使用这个游戏来打发时间。 游戏的棋盘被分割成3x3的区域,上面放着标记有1~8八个数字的方形棋子,剩下一个区域为空。 ...
  • kakitgogogo
  • kakitgogogo
  • 2017-03-31 20:06
  • 109

【hihocoder 1312】搜索三·启发式搜索(启发式搜索写法)

【题目链接】:http://hihocoder.com/problemset/problem/1312?sid=1092363【题意】 【题解】 定义一个A*函数 f = step+val 这里的val是当前这个状态;每个点到目标状态的点的曼哈顿距离的绝对值; (这个值肯定比真正需要花...
  • harlow_cheng
  • harlow_cheng
  • 2017-04-30 09:58
  • 172

hiho一下 第一百周 #1312 : 搜索三·启发式搜索 【康托展开-压缩】

#1312 : 搜索三·启发式搜索 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 在小Ho的手机上有一款叫做八数码的游戏,小Ho在坐车或者等人的时候经常使用这个游戏来打发时间。 游戏的棋盘被分割成3x3...
  • leibniz_zhang
  • leibniz_zhang
  • 2016-11-07 17:25
  • 529

【启发式搜索】A*与IDA*学习笔记

搞了这么久发现自己到现在还不会启发式搜索ヾ(。`Д´。)所以今天正好趁着搜索练习题的风去搞了启发式搜索 A*搜索算法,俗称A星算法。这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。常用于游戏中的NPC的移动计算,或在线游戏的BOT的移动计算上。 该算法像Dijk...
  • CreationAugust
  • CreationAugust
  • 2014-12-14 21:48
  • 1500

A*启发式搜索算法详解 人工智能

A*启发式搜索算法详解 人工智能 转自:http://dev.gameres.com/Program/Abstract/Arithmetic/AmitAStar.mht 我们尝试解决的问题是把一个游戏对象(game object)从出发点移动到目的地。路径搜索(Pathfinding)的目标是找到一...
  • sxy_cnyali
  • sxy_cnyali
  • 2016-02-01 01:46
  • 2960

八数码游戏分析+源码——启发式搜索(二)

大家通过阅读 八数码游戏分析——启发式搜索(一) 应该对解决八数码的启发式搜索算法有了一个大致的印象了,那么我就开始介绍基于 八数码游戏分析——启发式搜索(一)的一种改进的算法。 改进的启发式搜索策略:八数码的下一个格局中每个棋子移动到正确位置所需要的步数要少于当前格局中每个棋
  • q345852047
  • q345852047
  • 2011-07-23 19:55
  • 3965

ACM:搜索算法专题(3)——启发式搜索

题目来源:          HihoCoder1312 题目描述:     给出一个九宫格的拼图游戏的棋局,求完成拼图最少需要一定的步数。 解答: ·规则:...
  • octopusflying
  • octopusflying
  • 2016-06-07 14:25
  • 2948

八数码问题-启发式搜索(A*算法)

八数码问题是这样一个问题。有一个3x3大小的棋盘,上面放着标记有1~8八个数字的方形棋子,剩下一个区域为空。 每一次只能移动一个棋子到相邻的空区域上,8个棋子都移动到如下图所示的位置时,就结束了。
  • qq_32400847
  • qq_32400847
  • 2016-07-03 22:40
  • 3860

A*启发式搜索

A*算法,作为启发式算法中很重要的一种,被广泛应用在最优路径求解和一些策略设计的问题中。而A*算法最为核心的部分,就在于它的一个估值函数的设计上: f(n)=g(n)+h(n)        其中f(n)是每个可能试探点的估值,它有两...
  • u013008291
  • u013008291
  • 2015-02-15 11:56
  • 817

【hihocoder 1312】搜索三·启发式搜索(普通广搜做法)

【题目链接】:http://hihocoder.com/problemset/problem/1312?sid=1092352【题意】 【题解】 从末状态的123456780开始逆向搜; 看它能到达哪些状态; 到时候O(1)输出就可以了; 用map< int,int> di...
  • harlow_cheng
  • harlow_cheng
  • 2017-04-30 05:50
  • 302
    个人资料
    • 访问:207780次
    • 积分:7068
    • 等级:
    • 排名:第3865名
    • 原创:522篇
    • 转载:3篇
    • 译文:0篇
    • 评论:12条
    文章分类
    最新评论