您的位置:首页 > 其它

UVa 1601 例题7-9 万圣节后的早晨

2015-10-03 17:41 253 查看
分析:如果以当前3个小写字母的位置为状态,则问题转化为图上的最短路问题。但是如果每次都判断小写字母的下一步是否合法,那就是说每次移动都需要判断5^3,肯定会超时。

方法是把所有可以移动的格子找出来建立一张图,就是把障碍物给删除,统计每个可以空格或者有鬼的格子可以移动到哪些格子,这样在判断的时候就节省了许多时间。然后bfs找最短路。

还有一种方法是双向宽度搜索,方法是起始状态和末尾状态都搜索,直到到达同一个状态,就是说小写字母也移动,大写字母也移动,直到二者移动到同一个状态,然后步数相加,这样的话时间复杂度肯定会降低。双搜大概是这个意思吧,以后有空了在实现吧。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int maxn=150,maxs=20;
const int dx[]={-1,1,0,0,0};
const int dy[]={0,0,1,-1,0};
inline int ID(int a,int b,int c)
{
return (a<<16)|(b<<8)|c;
}
int s[3],t[3]; //保存初末状态
int deg[maxn];//某个格子有多少个相连的格子
int G[maxn][5];//保存某个格子可以用到哪些格子
inline bool conflict(int a,int b,int a2,int b2)
{
return a2==b2||(a2==b&&b2==a); //如果两个鬼移动到同一个位置或者位置互换则冲突
}
int d[maxn][maxn][maxn]; //走到某个状态经过的步数
int bfs()
{
queue<int> q;
memset(d,-1,sizeof(d));
q.push(ID(s[0],s[1],s[2])); //每个状态给他编号来判断是否访问过
d[s[0]][s[1]][s[2]]=0;
while(!q.empty()){
int u=q.front();q.pop();
int a=(u>>16)&0xff,b=(u>>8)&0xff,c=u&0xff;
// cout<<a<<" "<<b<<" "<<c<<endl;
if(a==t[0]&&b==t[1]&&c==t[2])return d[a][b][c];
for(int i=0;i<deg[a];i++){
int a2=G[a][i];
for(int j=0;j<deg[b];j++){
int b2=G[b][j];
if(conflict(a,b,a2,b2))continue;
for(int k=0;k<deg[c];k++){
int c2=G[c][k];
if(conflict(a,c,a2,c2))continue;
if(conflict(b,c,b2,c2))continue;
if(d[a2][b2][c2]!=-1)continue;
d[a2][b2][c2]=d[a][b][c]+1;
q.push(ID(a2,b2,c2));
}
}
}
}
return -1;
}
int main()
{
int w,h,n;
//freopen("f.txt","r",stdin);
while(scanf("%d%d%d\n",&w,&h,&n)==3&&n){
char maze[20][20];
for(int i=0;i<h;i++)
fgets(maze[i],20,stdin);
int cnt,x[maxn],y[maxn],id[maxn][maxn];
cnt=0;
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
if(maze[i][j]!='#'){ //把障碍去掉,建图
x[cnt]=i,y[cnt]=j,id[i][j]=cnt;
if(islower(maze[i][j]))s[maze[i][j]-'a']=cnt;
else if(isupper(maze[i][j]))t[maze[i][j]-'A']=cnt;
cnt++;
}
}
}
for(int i=0;i<cnt;i++){
deg[i]=0;
for(int dir=0;dir<5;dir++){
int nx=x[i]+dx[dir],ny=y[i]+dy[dir];
if(maze[nx][ny]!='#')G[i][deg[i]++]=id[nx][ny];//统计出每个位置可以移动到的位置
}
}
  //n==1||n==2时,把格子加上,凑够三个,初末位置重合
if(n<=2){
deg[cnt]=1;G[cnt][0]=cnt;s[2]=t[2]=cnt++;
}
if(n<=1){
deg[cnt]=1;G[cnt][0]=cnt;s[1]=t[1]=cnt++;
}
printf("%d\n",bfs());

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: