题目:http://poj.org/problem?id=1077
题目大意:给你一个3*3的矩阵,有数字1~8,还有一个x,x能往周围移动,一移动会与目标格子的数字交换。求出能把矩阵变成1 2 3 / 4 5 6 / 7 8 x 的一个方案,如果不行,就输出“unsolvable”。
解题思路:很不错的搜索题!实现真的比较麻烦。首先,广搜,要保存每个节点的状态,状态还要判重,所以这里要hash。这里是数字的排列,那么用康拓展开来弄。还有就是记录路径,我用的是直接在节点里用string来完全记录路径,也可以开数组村每个状态的上一个状态和转到他的方向,相对来说是后面一种比较快。
这道题目,我是用裸BFS做的,话说可以双向BFS,A*,IDA*。双向BFS见下文。A*和IDA*还没学,学了再来。。
裸BFS代码如下:(写的比较挫,C++ TLE,G++ AC,估计就会记录路径那里每次都要复制路径,比较耗时 = = )
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN = 12;//int 最多 12!
struct Cantor
{
int n,k;
int fac[MAXN],hh[MAXN];//hh为标记数组
void init(int n)
{
this->n = n;
fac[0] = 1;
for(int i = 1;i <= MAXN;i++)
fac[i] = fac[i-1]*i;
}
int code(int* s)//康拓展开
{
int num = 0;
for(int i = 0;i < n;i++)
{
int cnt = 0;
for(int j = i+1;j < n;j++)
if(s[i] > s[j]) cnt++;//求逆序数
num += cnt*fac[n-1-i];
}
return num+1;
}
void decode(int k)//逆康拓展开
{
k -= 1;//第k大,那么比他小的有k-1个
memset(hh,0,sizeof(hh));
int a = k,b;
for(int i = n-1;i >= 0;i--)
{
b = a/fac[i];
a = a%fac[i];
int cnt = 0;
int j;
for(j = 1;cnt < b && j <= n;j++)
if(hh[j] == 0)
cnt++;
while(hh[j] == 1) j++;
hh[j] = 1;
printf("%d ",j);//打印的序列从 1 开始
}
puts("");
}
}ct;
int s[11];
struct Node
{
int x,y,st;
int ss[11];
string path;
};
bool vis[444444];
int dir_x[] = {-1,0,1,0},dir_y[] = {0,1,0,-1};
char index[] = {'u','r','d','l'};
queue<Node> q;
int ok = 0;
void bfs(int s_x,int s_y,int start)
{
memset(vis,0,sizeof(vis));
while(!q.empty()) q.pop();
Node cur;
cur.x = s_x;cur.y = s_y;cur.st = start;
for(int i = 0;i < 9;i++) cur.ss[i] = s[i];
q.push(cur);
vis[start] = 1;
while(!q.empty())
{
Node cur = q.front();q.pop();
if(cur.st == 1)
{
cout<<cur.path<<endl;
ok = 1;
break;
}
for(int dd = 0;dd < 4;dd++)
{
int xx = cur.x+dir_x[dd];
int yy = cur.y+dir_y[dd];
if(xx < 0 || xx > 2 || yy < 0 || yy > 2) continue;
Node next = cur;
next.ss[xx*3+yy] = 9;
next.ss[cur.x*3+cur.y] = cur.ss[xx*3+yy];
next.x = xx;next.y = yy;
next.st = ct.code(next.ss);
//for(int i = 0;i < 9;i++) printf("%d ",next.ss[i]);
//puts("");
if(!vis[next.st])
{
next.path += index[dd];
q.push(next);
vis[next.st] = 1;
}
}
}
}
int main()
{
ct.init(9);
char str[11];
int s_x,s_y;
for(int i = 0;i < 9;i++)
{
scanf("%s",str);
if(str[0] == 'x')
{
s_x = i/3;
s_y = i%3;
s[i] = 9;
}
else s[i] = str[0]-'0';
}
int start = ct.code(s);
ok = 0;
bfs(s_x,s_y,start);
if(!ok) cout<<"unsolvable"<<endl;
return 0;
}
/*
1 2 3 x 4 6 7 5 8
rdr
*/
由于起点和终点都很清楚,那么可以用双向BFS,效率会高很多。
双向BFS其实和BFS一样,就是代码量多了一倍。。
关于双向BFS,有一点需要说明,不是两边点交替,而是两边层交替,点交替求出来可能不是最短路径。这里我的实现方法是0、1交替来标记不同的层。
在路径输出方面,这次我用了数组标记的方法,即标记每个状态是由哪个状态得到的,后面用递归输出。
双向BFS代码如下:(50ms以内了,效率果然高了很多)
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN = 12;//int 最多 12!
struct Cantor
{
int n,k;
int fac[MAXN],hh[MAXN];//hh为标记数组
void init(int n)
{
this->n = n;
fac[0] = 1;
for(int i = 1;i <= MAXN;i++)
fac[i] = fac[i-1]*i;
}
int code(int* s)//康拓展开
{
int num = 0;
for(int i = 0;i < n;i++)
{
int cnt = 0;
for(int j = i+1;j < n;j++)
if(s[i] > s[j]) cnt++;//求逆序数
num += cnt*fac[n-1-i];
}
return num+1;
}
void decode(int k)//逆康拓展开
{
k -= 1;//第k大,那么比他小的有k-1个
memset(hh,0,sizeof(hh));
int a = k,b;
for(int i = n-1;i >= 0;i--)
{
b = a/fac[i];
a = a%fac[i];
int cnt = 0;
int j;
for(j = 1;cnt < b && j <= n;j++)
if(hh[j] == 0)
cnt++;
while(hh[j] == 1) j++;
hh[j] = 1;
printf("%d ",j);//打印的序列从 1 开始
}
puts("");
}
}ct;
int s[11];
typedef pair<int,char> pr;
pr path1[444444],path2[444444];
struct Node
{
int x,y,st;
int ss[11];
int flag;
};
bool vis1[444444],vis2[444444];
int dir_x[] = {-1,0,1,0},dir_y[] = {0,1,0,-1};
char index1[] = {'u','r','d','l'},index2[] = {'d','l','u','r'};
queue<Node> q1,q2;
int start,ed;
void print_path1(int st)
{
if(st == start)
{
return ;
}
print_path1(path1[st].first);
printf("%c",path1[st].second);
}
void print_path2(int st)
{
if(st == ed)
{
printf("%c",path2[st].second);
return ;
}
printf("%c",path2[st].second);
print_path2(path2[st].first);
}
int bfs1(int flag)
{
while(!q1.empty() && q1.front().flag != flag)
{
Node cur = q1.front();q1.pop();
for(int dd = 0;dd < 4;dd++)
{
int xx = cur.x+dir_x[dd];
int yy = cur.y+dir_y[dd];
if(xx < 0 || xx > 2 || yy < 0 || yy > 2) continue;
Node next = cur;
next.ss[xx*3+yy] = 9;
next.ss[cur.x*3+cur.y] = cur.ss[xx*3+yy];
next.x = xx;next.y = yy;
next.st = ct.code(next.ss);
if(vis2[next.st])
{
print_path1(cur.st);
printf("%c",index1[dd]);
print_path2(next.st);
puts("");
return 1;
}
if(!vis1[next.st])
{
path1[next.st] = make_pair(cur.st,index1[dd]);
next.flag = flag;
q1.push(next);
vis1[next.st] = 1;
}
}
}
return 0;
}
int bfs2(int flag)
{
while(!q2.empty() && q2.front().flag != flag)
{
Node cur = q2.front();q2.pop();
for(int dd = 0;dd < 4;dd++)
{
int xx = cur.x+dir_x[dd];
int yy = cur.y+dir_y[dd];
if(xx < 0 || xx > 2 || yy < 0 || yy > 2) continue;
Node next = cur;
next.ss[xx*3+yy] = 9;
next.ss[cur.x*3+cur.y] = cur.ss[xx*3+yy];
next.x = xx;next.y = yy;
next.st = ct.code(next.ss);
if(vis1[next.st])
{
print_path1(next.st);
printf("%c",index2[dd]);
print_path2(cur.st);
puts("");
return 1;
}
if(!vis2[next.st])
{
path2[next.st] = make_pair(cur.st,index2[dd]);
next.flag = flag;
q2.push(next);
vis2[next.st] = 1;
}
}
}
return 0;
}
int bibfs(int s_x,int s_y,int e_x,int e_y)
{
if(start == ed) return 1;
memset(vis1,0,sizeof(vis1));
while(!q1.empty()) q1.pop();
Node cur;
cur.x = s_x;cur.y = s_y;cur.st = start;
for(int i = 0;i < 9;i++) cur.ss[i] = s[i];
cur.flag = 0;
q1.push(cur);
vis1[start] = 1;
memset(vis2,0,sizeof(vis2));
while(!q2.empty()) q2.pop();
cur.x = e_x;cur.y = e_y;cur.st = ed;
for(int i = 0;i < 9;i++) cur.ss[i] = i+1;
cur.flag = 0;
q2.push(cur);
vis2[ed] = 1;
int flag1 = 0,flag2 = 0;
while(!q1.empty() && !q2.empty())
{
if(q1.size() < q2.size())
{
if(bfs1(flag1 ^= 1)) return 1;
}
else
{
if(bfs2(flag2 ^= 1)) return 1;
}
}
return 0;
}
int main()
{
ct.init(9);
char str[11];
int s_x,s_y;
for(int i = 0;i < 9;i++)
{
scanf("%s",str);
if(str[0] == 'x')
{
s_x = i/3;
s_y = i%3;
s[i] = 9;
}
else s[i] = str[0]-'0';
}
start = ct.code(s);
ed = 1;
if(!bibfs(s_x,s_y,2,2))
cout<<"unsolvable"<<endl;
return 0;
}
/*
1 2 3 x 4 6 7 5 8
rdr
*/