【hdu1043 && poj 1077】八数码问题
题目大意: 给你一个九宫格的初始状态(一个空格+数字1~8),让你按规则走变成目标状态(数字按顺序排序+空格在最后),如果有这样的解法输出其中一种少操作步骤解法,否则输出unsolvable。
解题思路:超级经典的题目,解法超级多(据说有八重解法,和乾坤大挪移一样的,越到后面说明你越厉害),本来属于弱菜,只能达到三重。
虽然上面两题题目是一样的,但是用的解法不一样,因为....蛋都碎了。
poj1077:康托展开+双向bfs(32ms)
数据比较弱,很多人都是用正向bfs水过去的,这里我用的是双向bfs,何谓双向bfs?简单的说就是起始状态和目标状态一起搜,轮着来。当在搜的过程中发现某个状态已经被另一不同开始状态搜过时,此时为最优解,即最优路径。双向bfs比单向bfs无论在时间上还是空间上都节省很多。
联想一下bfs是从某一个点向四周扩散的,给你看个图就理解了。
这里还要介绍一下如何保存状态:
方法1:0-8,总共也就9位数,我们可以对应它进行int压缩,比如格子数从左到右从上到下为 2 3 1 5 x 8 4 6 7 , 可以将它进行压缩为2 3 1 5 8 4 6 7 5(x空格在第5位放在个位对应数字5,其余的对应int数前8位数)。
方法2:9位数总共有9!个状态,可以利用到康托展开,何谓康托展开?比如数3 2 1 ,问你321在序列1 2 3全排列中是第几大,第一位是3,后面比它小的有1、2,则有2*2!,第二位数是2,后面比它小的有1,则有1*1!,加起来就是2*2!+1*1!=5,所以是第五大了。(具体:here)
1 #include <iostream> 2 #include <cstdio> 3 #include <queue> 4 #include <algorithm> 5 #include <cstring> 6 using namespace std; 7 8 const int maxn=366666; 9 int visit1[maxn], visit2[maxn]; 10 int pp[9]= {1,1,2,6,24,120,720,5040,40430}; 11 int dir[4][2]= {0,1,0,-1,-1,0,1,0}; 12 char dd[4]= {'r','l','u','d'}; 13 int tp[maxn], st_aim, sd_aim; 14 15 struct node 16 { 17 int pos0; 18 int state; 19 int s[9]; 20 }; 21 22 struct path 23 { 24 int pre; 25 int dir; 26 }path1[maxn], path2[maxn]; 27 28 int cantor(int ss[]) 29 { 30 int sum=0; 31 for(int i=0; i<9; i++) 32 { 33 int num=0; 34 for(int j=i+1; j<9; j++) 35 if(ss[j]<ss[i]) num++; 36 sum+=num*pp[8-i]; 37 } 38 return sum; 39 } 40 41 char change(char c) 42 { 43 if(c=='u') return 'd'; 44 else if(c=='d') return 'u'; 45 else if(c=='l') return 'r'; 46 else if(c=='r') return 'l'; 47 } 48 49 void output1(int state) 50 { 51 int num=0; 52 while(state!=st_aim) tp[num++]=path1[state].dir, state=path1[state].pre; 53 for(int i=num-1; i>=0; i--) printf("%c",tp[i]); 54 } 55 56 void output2(int state) 57 { 58 while(state!=sd_aim) printf("%c",change(path2[state].dir)), state=path2[state].pre; 59 puts(""); 60 } 61 62 bool bfs(int ss[]) 63 { 64 queue<node>p,q; 65 node cur, tmp; 66 memset(visit1,0,sizeof(visit1)); 67 memset(visit2,0,sizeof(visit2)); 68 for(int i=0; i<9; i++) 69 { 70 cur.s[i]=ss[i]; 71 if(ss[i]==0) cur.pos0=i; 72 } 73 cur.state=cantor(cur.s); 74 q.push(cur); 75 visit1[cur.state]=1, st_aim=cur.state; 76 for(int i=0; i<8; i++) cur.s[i]=i+1; 77 cur.s[8]=0, cur.pos0=8; 78 cur.state=cantor(cur.s); 79 p.push(cur); 80 if(visit1[cur.state]) { puts(""); return true; } 81 visit2[cur.state]=1, sd_aim=cur.state; 82 while(!q.empty()&&!p.empty()) 83 { 84 if(!q.empty()) 85 { 86 tmp=q.front(); 87 q.pop(); 88 for(int k=0; k<4; k++) 89 { 90 int x=tmp.pos0/3+dir[k][0]; 91 int y=tmp.pos0%3+dir[k][1]; 92 if(x>=0&&x<=2&&y>=0&&y<=2) 93 { 94 for(int i=0; i<9; i++) cur.s[i]=tmp.s[i]; 95 cur.pos0=3*x+y; 96 swap(cur.s[cur.pos0],cur.s[tmp.pos0]); 97 cur.state=cantor(cur.s); 98 if(visit2[cur.state]) 99 { 100 output1(tmp.state); 101 printf("%c",dd[k]); 102 output2(cur.state); 103 return true; 104 } 105 if(!visit1[cur.state]) 106 { 107 visit1[cur.state]=1; 108 path1[cur.state].pre=tmp.state; 109 path1[cur.state].dir=dd[k]; 110 q.push(cur); 111 } 112 } 113 } 114 } 115 if(!p.empty()) 116 { 117 tmp=p.front(); 118 p.pop(); 119 for(int k=0; k<4; k++) 120 { 121 int x=tmp.pos0/3+dir[k][0]; 122 int y=tmp.pos0%3+dir[k][1]; 123 if(x>=0&&x<=2&&y>=0&&y<=2) 124 { 125 for(int i=0; i<9; i++) cur.s[i]=tmp.s[i]; 126 cur.pos0=3*x+y; 127 swap(cur.s[cur.pos0],cur.s[tmp.pos0]); 128 cur.state=cantor(cur.s); 129 if(visit1[cur.state]) 130 { 131 output1(cur.state); 132 printf("%c",change(dd[k])); 133 output2(tmp.state); 134 return true; 135 } 136 if(!visit2[cur.state]) 137 { 138 visit2[cur.state]=1; 139 path2[cur.state].pre=tmp.state; 140 path2[cur.state].dir=dd[k]; 141 p.push(cur); 142 } 143 } 144 } 145 } 146 } 147 return false; 148 } 149 150 int main() 151 { 152 char str[30]; 153 int s[10]; 154 while(gets(str)) 155 { 156 int num=0; 157 for(int i=0; str[i]; i++) 158 { 159 if(str[i]>='1'&&str[i]<='8') s[num++]=str[i]-'0'; 160 else if(str[i]=='x') s[num++]=0; 161 } 162 bool ok=bfs(s); 163 if(!ok) puts("unsolvable"); 164 } 165 } 166 // 1 2 3 4 x 6 7 5 8
hdu1043: 康托展开+逆序bfs(打表) (296ms) (为什么我写的双向bfs在这里过不了,难道是我写搓了?)
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <algorithm> 5 #include <cstring> 6 using namespace std; 7 8 const int maxn=366666; 9 int visit[maxn]; 10 int pp[9]= {1,1,2,6,24,120,720,5040,40430}; 11 int dir[4][2]= {0,1,0,-1,-1,0,1,0}; 12 char dd[4]= {'r','l','u','d'}; 13 string path[maxn]; 14 15 struct node 16 { 17 int pos0; 18 int state; 19 int s[9]; 20 string path; 21 } que[maxn]; 22 23 int cantor(int ss[]) 24 { 25 int sum=0; 26 for(int i=0; i<9; i++) 27 { 28 int num=0; 29 for(int j=i+1; j<9; j++) 30 if(ss[j]<ss[i]) num++; 31 sum+=num*pp[8-i]; 32 } 33 return sum; 34 } 35 36 void bfs() 37 { 38 int l=0, h=0; 39 node cur, tmp; 40 memset(visit,0,sizeof(visit)); 41 for(int i=0; i<8; i++) cur.s[i]=i+1; 42 cur.s[8]=0, cur.pos0=8; 43 cur.path=""; 44 cur.state=cantor(cur.s); 45 que[l++]=cur; 46 visit[cur.state]=1; 47 path[cur.state]=cur.path; 48 while(l!=h) 49 { 50 tmp=que[h++]; 51 for(int k=0; k<4; k++) 52 { 53 int x=tmp.pos0/3+dir[k][0]; 54 int y=tmp.pos0%3+dir[k][1]; 55 if(x>=0&&x<=2&&y>=0&&y<=2) 56 { 57 for(int i=0; i<9; i++) cur.s[i]=tmp.s[i]; 58 cur.pos0=3*x+y; 59 // cout << cur.pos0 << "---" << tmp.pos0 << "----" << cur.path << "---"<<dd[k]<<endl; 60 swap(cur.s[cur.pos0],cur.s[tmp.pos0]); 61 cur.state=cantor(cur.s); 62 if(!visit[cur.state]) 63 { 64 cur.path=tmp.path+dd[k]; 65 visit[cur.state]=1; 66 que[l++]=cur; 67 path[cur.state]=cur.path; 68 } 69 } 70 } 71 } 72 } 73 74 void output(string s) 75 { 76 int len=s.size(); 77 for(int i=len; i>=0; i--) 78 { 79 if(s[i]=='u') printf("d"); 80 else if(s[i]=='d') printf("u"); 81 else if(s[i]=='l') printf("r"); 82 else if(s[i]=='r') printf("l"); 83 } 84 puts(""); 85 } 86 87 int main() 88 { 89 char str[30]; 90 int s[10]; 91 bfs(); 92 while(gets(str)) 93 { 94 int num=0; 95 for(int i=0; str[i]; i++) 96 { 97 if(str[i]>='1'&&str[i]<='8') s[num++]=str[i]-'0'; 98 else if(str[i]=='x') s[num++]=0; 99 } 100 int s_state=cantor(s); 101 if(!visit[s_state]) puts("unsolvable"); 102 else output(path[s_state]); 103 } 104 }
因为此题太经典,所以在此占个坑,以后慢慢来攻破这八层境界(A*+IDA*+>>>>)。