一组数据 ,正向搜索。 poj 1077
代码如下: A* + hash +堆 + 曼哈顿距离 做一组数据的poj1077,可是,但是对于hdu 1043多组数据,没有剪枝,故超时,下面再给剪枝改进的算法。
1 #include<iostream> 2 #include<stdlib.h> 3 #include<stdio.h> 4 #include<math.h> 5 #include<string.h> 6 #include<string> 7 #include<queue> 8 #include<algorithm> 9 #define N 363000 // 9! = 362880 10 using namespace std; 11 12 struct node{ 13 int ma[9]; 14 int ans1; //哈希值 15 int x; // 记录x可移动的位置,记为9 16 int f; // 估价函数 17 int g; // 深度 18 string path; 19 bool operator <(const node & a)const{ 20 return f>a.f; //优先访问估价函数值f较小的节点 21 } 22 }; 23 int visited[N]; 24 string path; 25 node start; 26 int end = 0; // 123456789对应的哈希值 27 int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}}; // 上-右-下-左 28 char index[5]="urdl"; 29 // 哈希表,排列逆序,用于判重 30 int fact[] = {1, 1, 2, 6, 24,120 ,720, 5040, 40320}; // 康托展开数列 31 //0! 1! 2! 3! 4! 5! 6! 7! 8! 32 int hash(int *s) // 返回每个排列的哈希值 33 { 34 int sum=0; 35 for(int i=0;i< 9;i++) 36 { 37 int num=0; // 计算 第i位数的逆序数 38 for(int j=0;j<i;j++) 39 if(s[i]<s[j]) 40 num++; 41 sum+= num * fact[i]; // sum的取值范围是 0---9!-1 42 } 43 return sum; 44 } 45 int ABS(int x){return x>0?x:(-x);} //求绝对值 46 int h_juli(int *s) // 不算x时的 曼哈顿距离启发函数,计算的是 从第n个点到目标点的最小代价 47 { 48 int tx,ty,endx,endy,tmp; // 逆序搜索,从目标状态为起点 49 int sum=0; 50 for(int i=0;i<9;i++) 51 { 52 if(s[i]== 9) continue; 53 tmp=s[i]; 54 tx= i /3; 55 ty= i %3 ; 56 endx = (tmp-1)/3; 57 endy = (tmp-1)%3; 58 sum+=ABS(tx-endx) + ABS(ty- endy); 59 } 60 return sum; 61 } 62 63 //广度优先搜索 64 int bfs() 65 { 66 priority_queue<node>q; 67 node now,next; 68 int x ,y,ans; 69 now= start; 70 now.ans1=hash(now.ma); 71 now.path=""; 72 if(now.ans1 == end){ 73 path=now.path; 74 return 1; 75 } 76 visited[now.ans1] =1; // 访问起始节点 77 now.g = 0; // 深度置为0 78 now.f = h_juli(now.ma); 79 q.push(now); //将顶点now压入队列 80 while(!q.empty()) 81 { 82 now =q.top(); //取出队列头的顶点,设为now 83 q.pop(); 84 x= now.x /3; 85 y= now.x %3; 86 for(int i=0;i<4;i++) 87 { 88 int tx= x+dir[i][0]; 89 int ty= y+dir[i][1]; 90 if(ty<0 || ty>2 || tx<0 || tx>2) continue; 91 next=now; // next 为 now的邻接顶点 92 next.x= tx*3+ty; 93 next.ma[now.x] = now.ma[next.x]; 94 next.ma[next.x] = 9; 95 ans = hash(next.ma); 96 if(!visited[ans] ) //且next没有访问过,访问next顶点,对next赋值,并将next加入队列 97 { 98 visited[ans]=1; 99 next.ans1=ans; 100 next.g++; 101 next.f= next.g+h_juli(next.ma); 102 next.path+=index[i]; 103 if(next.ans1 == end){ 104 path=next.path; 105 return 1; 106 } 107 q.push(next); 108 } 109 110 } 111 112 } 113 return 0; 114 } 115 116 int main() 117 { 118 int i,j; 119 char ch; 120 while(cin>>ch) 121 { 122 if(ch=='x') 123 { 124 start.ma[0]=9; 125 start.x=0; 126 } 127 else 128 start.ma[0]=ch-'0'; 129 for( i=1;i<9;i++) 130 { 131 cin>>ch; 132 if(ch=='x') 133 { 134 start.ma[i]=9; 135 start.x=i; 136 } 137 else 138 start.ma[i]=ch-'0'; 139 } 140 start.ans1=hash(start.ma); 141 memset(visited,0,sizeof(visited)); 142 if(bfs()) 143 cout<<path<<endl; 144 else 145 146 printf("unsolvable "); 147 148 } 149 return 0; 150 }
但是 hdu 1043 多项数据输入,如果采用正向搜索,绝对超时。
反向搜索,把所有情况打表出来。
hash+ 打表+广搜
代码如下:
#include<iostream> #include<stdlib.h> #include<stdio.h> #include<math.h> #include<string.h> #include<string> #include<queue> #include<algorithm> #define N 363000 // 9! = 362880 using namespace std; struct node{ int ma[9]; int ans1; //哈希值 int x; // 记录x可移动的位置 string path; }; int visited[N]; string path[N]; node start; int end = 0; // 123456789对应的哈希值 int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}}; // 上-右-下-左 char index[5]="dlur"; // 下-左-上-右 , 反向搜索,用于打表 int fact[] = {1, 1, 2, 6, 24,120 ,720, 5040, 40320}; int hash(int *s) // 返回每个排列的哈希值 { int sum=0; for(int i=0;i< 9;i++) { int num=0; // 计算 第i位数的逆序数 for(int j=0;j<i;j++) if(s[i]<s[j]) num++; sum+= num * fact[i]; // sum的取值范围是 0---9!-1 } return sum; } //广度优先搜索 多项输入,打表,否则 超时 void bfs() { memset(visited,0,sizeof(visited)); node now,next; int x ,y,ans; for(int i=0;i<9;i++) now.ma[i]=i+1; now.x=8; now.ans1=end; now.path=""; queue<node>q; q.push(now); path[end]=""; while(!q.empty()) { now =q.front(); //取出队列头的顶点,设为now q.pop(); x= now.x /3; y= now.x %3; for(int i=0;i<4;i++) { int tx= x+dir[i][0]; int ty= y+dir[i][1]; if(ty<0 || ty>2 || tx<0 || tx>2) continue; next=now; // next 为 now的邻接顶点 next.x= tx*3+ty; next.ma[now.x] = now.ma[next.x]; next.ma[next.x] = 9; next.ans1 = hash(next.ma); if(!visited[next.ans1] ) { visited[next.ans1]=1; next.path=index[i]+next.path; //字符串反向存储,然后输出 q.push(next); path[next.ans1]=next.path; // 打表 } } } } int main() { int i,j; char ch; bfs(); while(cin>>ch) { if(ch=='x') { start.ma[0]=9; start.x=0; } else start.ma[0]=ch-'0'; for( i=1;i<9;i++) { cin>>ch; if(ch=='x') { start.ma[i]=9; start.x=i; } else start.ma[i]=ch-'0'; } start.ans1=hash(start.ma); if(visited[start.ans1]) // 在表里的点 cout<<path[start.ans1]<<endl; else printf("unsolvable "); } return 0; }
三:
hdu 1043 A* +哈密顿距离+ hash+剪枝 + 堆
代码如下:
#include<iostream> #include<stdlib.h> #include<stdio.h> #include<math.h> #include<string.h> #include<string> #include<queue> #include<algorithm> #define N 363000 // 9! = 362880 using namespace std; struct node{ int ma[9]; int ans1; //哈希值 int x; // 记录x可移动的位置,记为9 int f; // 估价函数 int g; // 深度 string path; bool operator <(const node & a)const{ return f>a.f; //优先访问估价函数值f较小的节点 } }; int visited[N]; string path; node start; int end = 0; // 123456789对应的哈希值 int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}}; // 上-右-下-左 char index[5]="urdl"; // 哈希表,排列逆序,用于判重 int fact[] = {1, 1, 2, 6, 24,120 ,720, 5040, 40320}; // 康托展开数列 //0! 1! 2! 3! 4! 5! 6! 7! 8! int hash(int *s) // 返回每个排列的哈希值 { int sum=0; for(int i=0;i< 9;i++) { int num=0; // 计算 第i位数的逆序数 for(int j=0;j<i;j++) if(s[i]<s[j]) num++; sum+= num * fact[i]; // sum的取值范围是 0---9!-1 } return sum; } int ABS(int x){return x>0?x:(-x);} //求绝对值 int h_juli(int *s) // 不算x时的 曼哈顿距离启发函数,计算的是 从第n个点到目标点的最小代价 { int tx,ty,endx,endy,tmp; // 逆序搜索,从目标状态为起点 int sum=0; for(int i=0;i<9;i++) { if(s[i]== 9) continue; tmp=s[i]; tx= i /3; ty= i %3 ; endx = (tmp-1)/3; endy = (tmp-1)%3; sum+=ABS(tx-endx) + ABS(ty- endy); } return sum; } //广度优先搜索 int bfs() { priority_queue<node>q; node now,next; int x ,y,ans; now= start; now.ans1=hash(now.ma); now.path=""; if(now.ans1 == end){ path=now.path; return 1; } visited[now.ans1] =1; // 访问起始节点 now.g = 0; // 深度置为0 now.f = h_juli(now.ma); q.push(now); //将顶点now压入队列 while(!q.empty()) { now =q.top(); //取出队列头的顶点,设为now q.pop(); x= now.x /3; y= now.x %3; for(int i=0;i<4;i++) { int tx= x+dir[i][0]; int ty= y+dir[i][1]; if(ty<0 || ty>2 || tx<0 || tx>2) continue; next=now; // next 为 now的邻接顶点 next.x= tx*3+ty; next.ma[now.x] = now.ma[next.x]; next.ma[next.x] = 9; ans = hash(next.ma); if(!visited[ans] ) //且next没有访问过,访问next顶点,对next赋值,并将next加入队列 { visited[ans]=1; next.ans1=ans; next.g++; next.f= next.g+h_juli(next.ma); next.path+=index[i]; if(next.ans1 == end){ path=next.path; return 1; } q.push(next); } } } return 0; } int check(int *s) // 剪枝 { int i, j,num=0; for( i=0;i<9;i++) { if(s[i] == 9) continue; for(j=0;j<i;j++) { if(s[j] ==9) continue; if(s[j] >s[i]) num++; } } return num&1; // 逆序数为偶数,返回0,否则返回1 } int main() { int i,j; char ch; while(cin>>ch) { if(ch=='x') { start.ma[0]=9; start.x=0; } else start.ma[0]=ch-'0'; for( i=1;i<9;i++) { cin>>ch; if(ch=='x') { start.ma[i]=9; start.x=i; } else start.ma[i]=ch-'0'; } if(check(start.ma)) {puts("unsolvable"); continue;} // 逆序数 为偶数,可达, 逆序数为奇数,不可达 start.ans1=hash(start.ma); memset(visited,0,sizeof(visited)); if(bfs()) cout<<path<<endl; else printf("unsolvable "); } return 0; }
排列哈希函数判重,参见:
http://blog.csdn.net/tiaotiaoyly/article/details/1720453
哈密顿距离 为 两个点 (x1,y1) (x2,y2) 距离= | x1 - x2| +| y1- y2|
A*算法
f(n)= g(n) + h(n) f(n)为估价函数 h(n) 为启发函数,是哈密顿距离 , g(n) 为节点n的深度