关于 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
- 关于 A* 和 IDA* 的讨论(先讨论一下A*,剩下留着慢慢写)
- A*和IDA*算法
- 迭代深搜+A*(IDA*)
- 关于String a=“”以及new String()和==以及equals的讨论
- 关于 (int&)a 的一些讨论
- A*算法 和 IDA*算法
- A* IDA*
- IDA*对A*的改进
- A星算法和 IDA星算法
- 关于多态:先随便写点东西~练练手,以后会慢慢见证一位大牛的成长~
- IDA*(迭代加深的A*算法) 八数码
- 关于a++和++a
- 关于 ( ++a )和( a++ )
- 关于 a-- 和 --a
- 关于a++和++a
- 关于a++和++a
- 关于a++ 和++a;
- 关于++a和a++的区别
- Writable接口
- 回调函数的作用
- 超小下载者源码
- Java引用传值的问题
- WPF 自定义Metro Style窗体
- 关于 A* 和 IDA* 的讨论(先讨论一下A*,剩下留着慢慢写)
- WPF 自定义Metro Style窗体
- 九度oj 1205
- 南邮 OJ 2029 节奏大师
- [C#]制作可以调整大小的自定义控件
- WPF 自定义Metro Style窗体
- HDU 1498 50 years, 50 colors 二分图最小点覆盖(基础题)
- MySQL性能优化的最佳20+条经验
- WPF 自定义Metro Style窗体