有N个修道士和N个野人准备渡河,但只有一条能容纳C人的小船,修道士的人数不得少于野人的人数(除非修道士的人数为0)。两种人都会划船,求一利用A*算法的方案。

解决方案 »

  1.   

    A*(A-Star)算法是一种静态路网中求解最短路最有效的方法。
      公式表示为: f(n)=g(n)+h(n), 
      其中f(n) 是节点n从初始点到目标点的估价函数,
      g(n) 是在状态空间中从初始节点到n节点的实际代价,
      h(n)是从n到目标节点最佳路径的估计代价。
      保证找到最短路径(最优解的)条件,关键在于估价函数h(n)的选取:
      估价值h(n)<= n到目标节点的距离实际值,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。
      如果 估价值>实际值, 搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
      估价值与实际值越接近,估价函数取得就越好。
      例如对于几何路网来说,可以取两节点间欧几理德距离(直线距离)做为估价值,即f=g(n)+sqrt((dx-nx)*(dx-nx)+(dy-ny)*(dy-ny));这样估价函数f在g值一定的情况下,会或多或少的受估价值h的制约,节点距目标点近,h值小,f值相对就小,能保证最短路的搜索向终点的方向进行。明显优于Dijstra算法的毫无无方向的向四周搜索。
      conditions of heuristic
      Optimistic (must be less than or equal to the real cost)
      As close to the real cost as possible
      主要搜索过程:
      创建两个表,OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点。
      遍历当前节点的各个节点,将n节点放入CLOSE中,取n节点的子节点X,->算X的估价值->
      While(OPEN!=NULL)
      {
      从OPEN表中取估价值f最小的节点n;
      if(n节点==目标节点) break;
      else
      {
      if(X in OPEN) 比较两个X的估价值f //注意是同一个节点的两个不同路径的估价值
      if( X的估价值小于OPEN表的估价值 )
      更新OPEN表中的估价值; //取最小路径的估价值
      if(X in CLOSE) 比较两个X的估价值 //注意是同一个节点的两个不同路径的估价值
      if( X的估价值小于CLOSE表的估价值 )
      更新CLOSE表中的估价值; 把X节点放入OPEN //取最小路径的估价值
      if(X not in both)
      求X的估价值;
      并将X插入OPEN表中; //还没有排序
      }
      将n节点插入CLOSE表中;
      按照估价值将OPEN表中的节点排序; //实际上是比较OPEN表内节点f的大小,从最小路径的节点向下进行。
      启发式搜索其实有很多的算法,比如:局部择优搜索法、最好优先搜索法等等。当然A*也是。这些算法都使用了启发函数,但在具体的选取最佳搜索节点时的策略不同。象局
      部择优搜索法,就是在搜索的过程中选取“最佳节点”后舍弃其他的兄弟节点,父亲节点,而一直得搜索下去。这种搜索的结果很明显,由于舍弃了其他的节点,可能也把最好的
      节点都舍弃了,因为求解的最佳节点只是在该阶段的最佳并不一定是全局的最佳。最好优先就聪明多了,他在搜索时,便没有舍弃节点(除非该节点是死节点),在每一步的估价
      中都把当前的节点和以前的节点的估价值比较得到一个“最佳的节点”。这样可以有效的防止“最佳节点”的丢失。那么A*算法又是一种什么样的算法呢?其实A*算法也是一种最
      好优先的算法。只不过要加上一些约束条件罢了。由于在一些问题求解时,我们希望能够求解出状态空间搜索的最短路径,也就是用最快的方法求解问题,A*就是干这种事情的!
      我们先下个定义,如果一个估价函数可以找出最短的路径,我们称之为可采纳性。A*算法是一个可采纳的最好优先算法。A*算法的估价函数可表示为:
      f'(n) = g'(n) + h'(n) 
      这里,f'(n)是估价函数,g'(n)是起点到终点的最短路径值,h'(n)是n到目标的最断路经的启发值。由于这个f'(n)其实是无法预先知道的,所以我们用前面的估价函数f(n)做
      近似。g(n)代替g'(n),但 g(n)>=g'(n)才可(大多数情况下都是满足的,可以不用考虑),h(n)代替h'(n),但h(n)<=h'(n)才可(这一点特别的重要)。可以证明应用这样的估价
      函数是可以找到最短路径的,也就是可采纳的。我们说应用这种估价函数的最好优先算法就是A*算法。哈。你懂了吗?肯定没懂。接着看。
      举一个例子,其实广度优先算法就是A*算法的特例。其中g(n)是节点所在的层数,h(n)=0,这种h(n)肯定小于h'(n),所以由前述可知广度优先算法是一种可采纳的。实际也是
      。当然它是一种最臭的A*算法。
      再说一个问题,就是有关h(n)启发函数的信息性。h(n)的信息性通俗点说其实就是在估计一个节点的值时的约束条件,如果信息越多或约束条件越多则排除的节点就越多,估价函
      数越好或说这个算法越好。这就是为什么广度优先算法的那么臭的原因了,谁叫它的h(n)=0,一点启发信息都没有。但在游戏开发中由于实时性的问题,h(n)的信息越多,它的计
      算量就越大,耗费的时间就越多。就应该适当的减小h(n)的信息,即减小约束条件。但算法的准确性就差了,这里就有一个平衡的问题。
      }
      

  2.   

    【标 题】:精简的A算法
    【作 者】:转载,不只作者是谁.有问题请与我联系
    我曾看过一些有关A*算法的程序,不过写得比较简洁、易懂的还是风云写的A*算法教学实例,但是这个算法并没有进行优化,该程序要用到实际应用中,还会有一定的限制, 所以我对该算法进行了改进,并加上更详细的算法说明,使其具有更好的教学作用和实用价值。开始前我先给出A*算法的基本思路: 
      问题:求出2D的迷宫中起始点S到目标点E的最短路径? 
      算法:  findpath() 
      { 
        把S点加入树根(各点所在的树的高度表示从S点到该点所走过的步数); 
        把S点加入排序队列(按该点到E点的距离排序+走过的步数从小到大排序); 
        1、排序队列sort_queue中距离最小的第一个点出列,并保存入store_queue中 
        2、从出列的点出发,分别向4个(或8个)方向中的一个各走出一步 
        3、并估算第2步所走到位置到目标点的距离,并把该位置加入树,最后把该点按距离从小到大排序后并放入队列
    中(由trytile函数实现) 
        4、如果该点从四个方向上都不能移动,则把该点从store_queue中删除 
        5、回到第一点,直到找到E点则结束    从目标点回溯树,直到树根则可以找到最佳路径,并保存在path[]中 
      }  文末附带的程序参考了风云的最短路径代码,并加以改进和优化:    把原来用于存放已处理节点的堆栈改为队列(store_queue),这样在从sort_queue队列出列时可直接放入
    store_queue中。 
        解除了地图大小的限制(如果有64K内存限制时,地图大小只能是180x180)。 
        删除了原程序中的一些冗余,见程序中的注释。 
        程序继续使用dis_map数组保存各点历史历史最佳距离,也包含了某点是否已经经过的信息,虽然这样做可能会比使用链表多用一些内存,但是在搜索时可以节省不时间。 
        程序更具有实用性,可直接或修改后运用于你的程序中,但请你使用该代码后 应该返回一些信息给我,如算法的改进或使用于什么程序等。 本程序可以用Borland C++或DJGPP编译,并附带有一个数据文件,保存有地图的数据。最后值得一提的是,该地图文件格式与风云的源代码的地图格式不一样。 
    /*-------------------------------------------------------------------------*///#define NDEBUG 
    #include 
    #include 
    #include 
    #include#define tile_num(x,y) ((y)*map_w+(x)) //将 x,y 坐标转换为地图上块的编号 
    #define tile_x(n) ((n)%map_w) //由块编号得出 x,y 坐标 
    #define tile_y(n) ((n)/map_w)#define MAPMAXSIZE 180 //地图面积最大为 180x180,如果没有64K内存限制可以更大 
    #define MAXINT 32767//树结构, 比较特殊, 是从叶节点向根节点反向链接,方便从叶节点找到根节点 
    typedef struct tree_node *TREE;struct tree_node { 
    int h; //节点所在的高度,表示从起始点到该节点所有的步数 
    int tile; //该节点的位置 
    TREE father; //该节点的上一步 
    };//链接结构,用于保存处理过的和没有处理过的结点 
    typedef struct link_node *LINK;struct link_node { 
    TREE node; 
    int f; 
    LINK next; 
    };LINK sort_queue; // 保存没有处理的行走方法的节点 
    LINK store_queue; // 保存已经处理过的节点 (搜索完后释放)unsigned char * map; //地图数据 
    unsigned int * dis_map; //保存搜索路径时,中间目标地最优解int map_w,map_h; //地图宽和高 
    int start_x,start_y,end_x,end_y; //地点,终点坐标// 初始化队列 
    void init_queue() 

    sort_queue=(LINK)malloc(sizeof(*sort_queue)); 
    sort_queue->node=NULL; 
    sort_queue->f=-1; 
    sort_queue->next=(LINK)malloc(sizeof(*sort_queue)); 
    sort_queue->next->node=NULL; 
    sort_queue->next->f=MAXINT; 
    sort_queue->next->next=NULL;store_queue=(LINK)malloc(sizeof(*store_queue)); 
    store_queue->node=NULL; 
    store_queue->f=-1; 
    store_queue->next=NULL; 
    }// 待处理节点入队列, 依*对目的地估价距离插入排序 
    void enter_queue(TREE node,int f) 

    LINK p=sort_queue,father,q; 
    while(f>p->f) { 
    father=p; 
    p=p->next; 
    assert(p); 

    q=(LINK)malloc(sizeof(*q)); 
    assert(sort_queue); 
    q->f=f,q->node=node,q->next=p; 
    father->next=q; 
    }// 将离目的地估计最近的方案出队列 
    TREE get_from_queue() 

    LINK bestchoice=sort_queue->next; 
    LINK next=sort_queue->next->next; 
    sort_queue->next=next;bestchoice->next=store_queue->next; 
    store_queue->next=bestchoice; 
    return bestchoice->node; 
    }// 释放栈顶节点 
    void pop_stack() 

    LINK s=store_queue->next; 
    assert(s); 
    store_queue->next=store_queue->next->next; 
    free(s->node); 
    free(s); 
    }// 释放申请过的所有节点 
    void freetree() 

    int i; 
    LINK p; 
    while(store_queue){ 
    p=store_queue; 
    free(p->node); 
    store_queue=store_queue->next; 
    free(p); 

    while (sort_queue) { 
    p=sort_queue; 
    free(p->node); 
    sort_queue=sort_queue->next; 
    free(p); 

    }// 估价函数,估价 x,y 到目的地的距离,估计值必须保证比实际值小 
    int judge(int x,int y) 

    int distance; 
    distance=abs(end_x-x)+abs(end_y-y); 
    return distance; 
    }// 尝试下一步移动到 x,y 可行否 
    int trytile(int x,int y,TREE father) 

    TREE p=father; 
    int h; 
    if (map[tile_num(x,y)]!=’ ’) return 1; // 如果 (x,y) 处是障碍,失败 
    //这一步用来判断(x,y)点是否已经加入队列,多余可以删除,因为dis_map已经 
    //保存该点是否已经保存 
    //while (p) { 
    // if (x==tile_x(p->tile) && y==tile_y(p->tile)) return 1; //如果 (x,y) 曾经经过,失败 
    // p=p->father; 
    //} 
    h=father->h+1; 
    if (h>=dis_map[tile_num(x,y)]) return 1; // 如果曾经有更好的方案移动到 (x,y) 失败 
    dis_map[tile_num(x,y)]=h; // 记录这次到 (x,y) 的距离为历史最佳距离// 将这步方案记入待处理队列 
    p=(TREE)malloc(sizeof(*p)); 
    p->father=father; 
    p->h=father->h+1; 
    p->tile=tile_num(x,y); 
    enter_queue(p,p->h+judge(x,y)); 
    return 0; 
    }// 路径寻找主函数 
    int * findpath(void) 

    TREE root; 
    int i,j; 
    int * path; 
    memset(dis_map,0xff,map_h*map_w*sizeof(*dis_map)); //填充dis_map为0XFF,表示各点未曾经过 
    init_queue(); 
    root=(TREE)malloc(sizeof(*root)); 
    root->tile=tile_num(start_x,start_y); 
    root->h=0; 
    root->father=NULL; 
    enter_queue(root,judge(start_x,start_y)); 
    for (;;) { 
    int x,y,child; 
    TREE p; 
    root=get_from_queue(); 
    if (root==NULL) { 
    return NULL; 

    x=tile_x(root->tile); 
    y=tile_y(root->tile); 
    if (x==end_x && y==end_y) break; // 达到目的地成功返回child=trytile(x,y-1,root); //尝试向上移动 
    child&=trytile(x,y+1,root); //尝试向下移动 
    child&=trytile(x-1,y,root); //尝试向左移动 
    child&=trytile(x+1,y,root); //尝试向右移动 
    //child&=trytile(x+1,y-1,root);//尝试向右上移动 
    //child&=trytile(x+1,y+1,root); //尝试向右下移动 
    //child&=trytile(x-1,y+1,root); //尝试向左下移动 
    //child&=trytile(x-1,y-1,root); //尝试向左上移动if (child!=0) 
    pop_stack(); // 如果四个方向均不能移动,释放这个死节点 
    }// 回溯树,将求出的最佳路径保存在 path[] 中 
    path=(int*)malloc((root->h+2)*sizeof(int)); 
    assert(path); 
    for (i=0;root;i++) { 
    path=root->tile; 
    root=root->father; 

    path=-1; 
    freetree(); 
    return path; 
    }void printpath(int *path) 

    int i; 
    if(path==NULL) return 
    for (i=0;path>=0;i++) { 
    gotoxy(tile_x(path)+1,tile_y(path)+1); 
    cprintf("."); 

    }int readmap() 

    FILE *f; 
    int i,j; 
    f=fopen("map.dat","r"); 
    assert(f); 
    fscanf(f,"%d,%d\n",&map_w,&map_h); 
    map=malloc(map_w*map_h+1); 
    assert(map); 
    for(i=0;i fgets(map+tile_num(0,i),map_w+2,f); 
    fclose(f); 
    start_x=-1,end_x=-1; 
    for (i=0;i for (j=0;j if (map[tile_num(j,i)]==’s’) map[tile_num(j,i)]=’ ’,start_x=j,start_y=i; 
    if (map[tile_num(j,i)]==’e’) map[tile_num(j,i)]=’ ’,end_x=j,end_y=i; 

    assert(start_x>=0 && end_x>=0); 
    dis_map=malloc(map_w*map_h*sizeof(*dis_map)); 
    assert(dis_map); 
    return 0; 
    }void showmap() 

    int i,j; 
    clrscr(); 
    for (i=0;i gotoxy(1,i+1); 
    for (j=0;j if (map[tile_num(j,i)]!=’ ’) cprintf("O"); 
    else cprintf(" "); 

    gotoxy(start_x+1,start_y+1); 
    cprintf("s"); 
    gotoxy(end_x+1,end_y+1); 
    cprintf("e"); 
    }int main() 

    int * path; 
    readmap(); 
    showmap(); 
    getch(); 
    path=findpath(); 
    printpath(path); 
    if(dis_map) free(dis_map); 
    if(path) free(path); 
    if(map) free(map); 
    getch(); 
    return 0;