关于 A* 和 IDA* 的讨论(先讨论一下A*,剩下留着慢慢写)

来源:互联网 发布:数据泄露防护系统破解 编辑:程序博客网 时间:2024/05/22 04:40

A*算法百度百科

IDA*算法百度百科

(上面两个觉得不够通俗易懂,可以看下面的)

A*算法,点我点我!

IDA*算法,点我点我!

最后,附上一个非常的经典的题目题解,觉得蛮屌的,先mark一下,以后慢慢学习学习!

八数码的八境界,点我点我!


接下来先讨论一下A*的算法, 具体理论知识还是以上面为主,接下来先附上一道经典题目knight moves的4种算法,分别是BFS,DFS,A*,数学方法。

在POJ上,在我没用A*时提交是43ms,而A*得算法是67ms,因为题目比较简单,所以猜想用高端的方法反而降低效率。

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <queue>using namespace std;int a,b;int c1,c2;char t[3],tt[3];bool vis[10][10];int dx[]={1,2,1,2,-1,-2,-1,-2};int dy[]={-2,-1,2,1,2,1,-2,-1};struct node{int x,y;int step;};bool judge(int x,int y){if(x<1||x>8||y<1||y>8)return false;if(vis[x][y])return false;return true;}queue<node>q;void bfs(){memset(vis,0,sizeof(vis));node s,e;while(!q.empty())q.pop();s.x=c1,s.y=a,s.step=0;q.push(s);vis[s.x][s.y]=1;while(!q.empty()){s=q.front();q.pop();if(s.x==c2&&s.y==b){printf("To get from %s to %s takes %d knight moves.\n",t,tt,s.step);break;}for(int i=0;i<8;i++){e.x=s.x+dx[i];e.y=s.y+dy[i];if(judge(e.x,e.y)){e.step=s.step+1;q.push(e);vis[e.x][e.y]=1;}}}}int main(){while(scanf("%s%s",t,tt)!=EOF)      {          c1=t[0]-'a'+1;a=t[1]-'0';          c2=tt[0]-'a'+1;b=tt[1]-'0';          bfs();      }  return 0;}

接下来是看了网上的解题报告,然后总结的解法:

#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <climits>const int MAX = 8;const int dirx[MAX] = {-2,-2,2,2,-1,-1,1,1},diry[MAX] = {1,-1,-1,1,2,-2,-2,2};int cstep[MAX][MAX],minx,dx,dy;void init(){int i,j;for(i=0;i<MAX;++i){for(j=0;j<MAX;++j){cstep[i][j] = INT_MAX;}}}void dfs(int x,int y,int cnt){if(x<0 || y<0 || x>=MAX || y>=MAX)return;if(x==dx && y==dy){if(cnt<minx)minx = cnt;return;}//通过下面注释掉的main方法,运行处8*8棋盘,任意两个点最短距离的最大值为6//所以加上这个剪枝,不加这个剪枝就超时if(cnt>6)return;if(cnt>cstep[x][y])return;cstep[x][y] = cnt;int i,tx,ty;for(i=0;i<MAX;++i){tx = x + dirx[i];ty = y + diry[i];dfs(tx,ty,cnt+1);}}int main(){//freopen("in.txt","r",stdin);char csx,csy,cdx,cdy;int sx,sy;while(scanf("%c%c %c%c%*c",&csy,&csx,&cdy,&cdx)!=EOF){sx = csx-'1';sy = csy-'a';dx = cdx-'1';dy = cdy-'a';minx = INT_MAX;init();dfs(sx,sy,0);printf("To get from %c%c to %c%c takes %d knight moves.\n",csy,csx,cdy,cdx,minx);}    return 0;}/*int main(){//freopen("out.txt","w",stdout);int i,j,ans;dx = 0,dy = 0;ans = -1;for(i=0;i<8;++i){for(j=0;j<8;++j){minx = INT_MAX;init();dfs(i,j,0);printf("%d,",minx);if(minx>ans)ans = minx;}}printf("\n%d\n",ans);return 0;}*/


下面这个代码是A*,是上面的A*算法入门的作者给的一种解:

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <queue>using namespace std; struct knight{    int x,y,step;    int g,h,f;    bool operator < (const knight & k) const{      //重载比较运算符    return f > k.f;    }}k;bool visited[8][8];                                //已访问标记(关闭列表)int x1,y1,x2,y2,ans;                               //起点(x1,y1),终点(x2,y2),最少移动次数ansint dirs[8][2]={{-2,-1},{-2,1},{2,-1},{2,1},{-1,-2},{-1,2},{1,-2},{1,2}};//8个移动方向priority_queue<knight> que;                        //最小优先级队列(开启列表) bool in(const knight & a){                         //判断knight是否在棋盘内    if(a.x<0 || a.y<0 || a.x>=8 || a.y>=8)        return false;    return true;}int Heuristic(const knight &a){                    //manhattan估价函数    return (abs(a.x-x2)+abs(a.y-y2))*10;}void Astar(){                                      //A*算法    knight t,s;    while(!que.empty()){        t=que.top(),que.pop(),visited[t.x][t.y]=true;        if(t.x==x2 && t.y==y2){            ans=t.step;            break;        }        for(int i=0;i<8;i++){            s.x=t.x+dirs[i][0],s.y=t.y+dirs[i][1];            if(in(s) && !visited[s.x][s.y]){                s.g = t.g + 23;                 //23表示根号5乘以10再取其ceil                s.h = Heuristic(s);                s.f = s.g + s.h;                s.step = t.step + 1;                que.push(s);            }        }    }}int main(){    char line[5];    while(gets(line)){        x1=line[0]-'a',y1=line[1]-'1',x2=line[3]-'a',y2=line[4]-'1';        memset(visited,false,sizeof(visited));        k.x=x1,k.y=y1,k.g=k.step=0,k.h=Heuristic(k),k.f=k.g+k.h;        while(!que.empty()) que.pop();        que.push(k);        Astar();    printf("To get from %c%c to %c%c takes %d knight moves.\n",line[0],line[1],line[3],line[4],ans);}    return 0;}


接下来是数学方法,引用自一位大神:

首先,对于两个点,只用考虑其横纵坐标的差值。比如a3,a4,横坐标差值为0,纵坐标差值为1现在设横纵坐标的差值分别是x,y由于马只能有8种方法,实际上只会出现4种(举个例子,本来方向向量有(1,2),(2,1),(1,-2),(2,-1),(-1,2),(-2,1),(-1,-2),(-2,-1),但是如果要最小的次数,就不可能同时出现(1,2)和(-1,-2),依次类推) 所以,我们设方向向量为(1,2),(2,1),(2,-1),(1,-2)的分别有a,b,c,d次,其中a,b,c,d可以为负数,a为负数代表方向向量为(-1,-2)于是,可以列两个方程:a+2b+2c+d=x 2a+b-c-2d=y我们要求的是|a|+|b|+|c|+|d|的最小值 首先把a,b,看做常量,解得c=(-4a-5b+2x+y)/3 d=(5a+4b-x-2y)/3那么有a+2b和2x+y模3同余 现在2x+y已知,对于b进行枚举,由于-n/2<=b<=n/2,进行枚举,对每个知道a模3是多少,进而再对可能的a进行枚举,从而解出c,d,进而求出总步数。 但是,特别要注意一点,就是角落的问题。 比如a1,b2按上面方法算的是2,实际是4.经过计算知,对于8*8的只有4种情况:a1 b2;a8 b7;g2 h1;g7 h8;对这四种情况单独拿出来说就好了。。 以上就是求解的数学方法。下面贴代码,对于原来的8*8题目的。 在poj2243上AC了,228K,32MS。n*n的稍作修改即可。--------------------------------#include <iostream> #include <string> using namespace std; int f(int a)    //就是abs(a),绝对值{     if (a<0)         return 0-a;     return a; } int main() {     string s1,s2;     int a,b,c,d,x,y,s,m;     while (cin >> s1 >> s2)     {         if ((s1=="a1" && s2=="b2") || (s1=="b2" && s2=="a1") || (s1=="g2" && s2=="h1") || (s1=="h1" && s2=="g2"))         {             cout << "To get from " << s1 << " to " << s2 << " takes 4 knight moves." << endl;             continue;         }         if ((s1=="a8" && s2=="b7") || (s1=="b7" && s2=="a8") || (s1=="g7" && s2=="h8") || (s1=="h8" && s2=="g7"))         {             cout << "To get from " << s1 << " to " << s2 << " takes 4 knight moves." << endl;             continue;         }         x=s2[0]-s1[0];    //横坐标差值        s=9999;         y=s2[1]-s1[1];    //纵坐标差值        for (b=-4;b<=4;b++)    //对b枚举for (b=-3;b<=3;b++)         {             m=y+2*x-2*b+30;    //a模3的余数            m%=3;             for (a=m-6;a<=m+6;a+=3)     //对a枚举            {                 c=(2*x+y-4*a-5*b)/3;    //求出c和d                 d=(5*a+4*b-x-2*y)/3;                 if (s>f(a)+f(b)+f(c)+f(d))                     s=f(a)+f(b)+f(c)+f(d);     //判断是否是最小的            }         }         cout << "To get from " << s1 << " to " << s2 << " takes " << s << " knight moves." << endl;     }     return 0; }



0 0
原创粉丝点击