uva1601(The morning after Helloween)解题报告

最先考虑到的当然是BFS,想当然地认为只要一个一个地BFS就可以了。就像是迷宫类的题目,一步一步找,最终找到了某一个状态就可以了,只不过人数多了点的走迷宫而已。但是如果这么做的话必然会超时,因为在每一个状态可以走的路太多,三个鬼,每个有5个方向可以走,三个加起来除去原地不动还有124种走法,而且算出来的最少步数也不一定少,第三组样例的步数就多达77步,空间有没有那么多就暂且不说了,时间会超,在uva上亲测过。所以必须要优化了。

首先就是受到了刘汝佳的提示了,他在书中说提取空格,因为‘#’占大多数,把空格提取出来就可以减少很多无用的搜索。

如果每次都判断小写字母的下一步是否合法,那就是说每次移动都需要判断5^3,超时了。

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

所以顺着这个思路就写了。当然也受到了刘汝佳的代码的熏陶了。
#include<string>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=16;
const int MAXM=192;
int getnum(int a, int b, int c)
{<span><span>  
</span></span>
	return (a<<16) | (b<<8) | c;<pre name="code" class="cpp"><span><span class="comment">//a,b,C不超256(16*16)不过是8位</span><span></span></span>

//映射ID方便运算 
 }int ok(int a,int b,int a1,int b1){return ((a1==b1)||(a1==b &&b1==a)); 任何一个鬼不能占用同一位置+剪枝 
}  
int cx[] = { -1, 1, 0, 0, 0 };int cy[] = { 0, 0, -1, 1, 0 };int id[MAXN + 10][MAXN + 10];int G[MAXM + 10][5];int st[3], ed[3];int edge[MAXM + 10], d[MAXM + 10][MAXM + 10][MAXM + 10];int w, h, n, cnt;char M[MAXN + 10][MAXN + 10];void bfs(){queue <int> q;memset(d, -1, sizeof(d));q.push(getnum(st[0], st[1], st[2])); //看队列前面的元素找最短路径
d[st[0]][st[1]][st[2]] = 0;while (!q.empty()){int u = q.front(); q.pop();int a = (u >> 16) & 255, b = (u >> 8) & 255, c = u & 255;if (a == ed[0] && b == ed[1] && c == ed[2]) return;for (int i = 1; i <= edge[a]; i++){int a1 = G[a][i];for (int j = 1; j <= edge[b]; j++){int b1 = G[b][j];if (ok(a, b, a1, b1))continue;for (int k = 1; k <= edge[c]; k++){int c1 = G[c][k];if (ok(a, c, a1, c1)) continue;if (ok(b, c, b1, c1)) continue;if (d[a1][b1][c1] == -1){d[a1][b1][c1] = d[a][b][c] + 1;q.push(getnum(a1, b1, c1));} //三重循环暴力所有状态}}}}}int main(){//freopen("bu.in","r",stdin);//freopen("bu.out","w",stdout);while (scanf("%d%d%d", &w, &h, &n) && n){char c = getchar();while (c != '\n')c = getchar();for (int i = 1; i <= h; i++)fgets(M[i] + 1, 20, stdin);cnt = 0;int x[MAXM + 10], y[MAXM + 10];for (int i = 1; i <= h; i++)for (int j = 1; j <= w; j++)if (M[i][j] != '#') //把障碍去掉,建图 
{id[i][j] = ++cnt;x[cnt] = i;y[cnt] = j;if ('a' <= M[i][j] && M[i][j] <= 'c')st[M[i][j] - 'a'] = cnt;if ('A' <= M[i][j] && M[i][j] <= 'C')ed[M[i][j] - 'A'] = cnt; //可以用来判断字符c是否为大写英文字母。 
}for (int i = 1; i <= cnt; i++){edge[i] = 0;for (int j = 0; j < 5; j++){int xx = x[i] + cx[j];int yy = y[i] + cy[j];if (M[xx][yy] != '#')
G[i][++edge[i]] = id[xx][yy];}}if (n <= 2){edge[++cnt] = 1;G[cnt][1] = cnt;st[2] = ed[2] = cnt;}if (n <= 1){edge[++cnt] = 1;G[cnt][1] = cnt;st[1] = ed[1] = cnt;} //n==1||n==2时,把格子加上,凑够三个,初末位置重合,所有情况均为三个鬼,方便 
bfs();printf("%d\n", d[ed[0]][ed[1]][ed[2]]);} return 0; }
 

当然刘汝佳也提到了用双向BFS,双向BFS的话从起点和终点同时双向一层一层地BFS,双向BFS使结点扩展由 a^(x)变为2*a^(x/2)。

//this is my test for that double directions to BFS.
#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
const int N=17;
const int maxn=200;
const int dx[]={1,0,-1,0,0};
const int dy[]={0,-1,0,1,0};
int n,m,k;
char tomymaze[N][N];
int x[maxn],y[maxn],cnt,mymap[maxn][5],degree[maxn],id[maxn][maxn];
int s[3],t[3];
int d[maxn][maxn][maxn],bd[maxn][maxn][maxn];
int ID(int a,int b,int c){
    return (a<<16)|(b<<8)|c;
}
void init(){
memset(d,-1,sizeof(d));
memset(bd,-1,sizeof(bd));
}
bool forcheck(int a,int b,int aa,int bb){
if(aa==b&&bb==a||aa==bb){
    return true;
}
return false;
}
queue<int> q;
bool frobfs(){
int value;
int uu=q.front();
int aa=(uu>>16)&0xff,bb=(uu>>8)&0xff,cc=uu&0xff;
value=d[aa][bb][cc];
while(!q.empty()){
    int u=q.front();
        int a=(u>>16)&0xff,b=(u>>8)&0xff,c=u&0xff;
        if(d[a][b][c]!=value){
                return false;
        }
        if(bd[a][b][c]!=-1){
                return true;
        }
        for(int i=0;i<degree[a];i++){
            int na=mymap[a][i];
            for(int j=0;j<degree[b];j++){
                int nb=mymap[b][j];
                for(int k=0;k<degree[c];k++){
                    int nc=mymap[c][k];
                    if(forcheck(a,b,na,nb)) continue;
                    if(forcheck(a,c,na,nc)) continue;
                    if(forcheck(b,c,nb,nc)) continue;
                    if(d[na][nb][nc]==-1){
                        d[na][nb][nc]=d[a][b][c]+1;
                        q.push(ID(na,nb,nc));
                    }
                }
            }
        }
        q.pop();
    }
return false;
}
queue<int> p;
bool behbfs(){
int value;
    int uu=p.front();
    int aa=(uu>>16)&0xff,bb=(uu>>8)&0xff,cc=uu&0xff;
    value=bd[aa][bb][cc];
while(!p.empty()){
        int u=p.front();
        int a=(u>>16)&0xff,b=(u>>8)&0xff,c=u&0xff;
        if(bd[a][b][c]!=value){
                return false;
        }
        if(d[a][b][c]!=-1){
                return true;
        }
        for(int i=0;i<degree[a];i++){
            int na=mymap[a][i];
            for(int j=0;j<degree[b];j++){
                int nb=mymap[b][j];
                for(int k=0;k<degree[c];k++){
                    int nc=mymap[c][k];
                    if(forcheck(a,b,na,nb)) continue;
                    if(forcheck(a,c,na,nc)) continue;
                    if(forcheck(b,c,nb,nc)) continue;
                    if(bd[na][nb][nc]==-1){
                        bd[na][nb][nc]=bd[a][b][c]+1;
                        p.push(ID(na,nb,nc));
                    }
                }
            }
        }
        p.pop();
}
return false;
}
int bfs(){
while(!q.empty()){
        q.pop();
}
while(!p.empty()){
        p.pop();
}
q.push(ID(s[0],s[1],s[2]));
p.push(ID(t[0],t[1],t[2]));
init();
d[s[0]][s[1]][s[2]]=0;
bd[t[0]][t[1]][t[2]]=0;
int step=0,ok=0;
while(!p.empty()&&!q.empty()){
     if(frobfs()){
         ok=1;
         break;
     }
     else{
        step++;
     }
     if(behbfs()){
         ok=1;
         break;
     }
     else{
         step++;
     }
}
return ok?step:-1;
}
int main()
{
    while(scanf("%d %d %d",&m,&n,&k)==3){
        if(!n&&!m&&!k){
                break;
        }
        gets(tomymaze[0]);
        cnt=0;
for(int i=0;i<n;i++){
            gets(tomymaze[i]);
for(int j=0; j<m; j++){
    if(tomymaze[i][j]!='#'){
        x[cnt]=i;
       y[cnt]=j;
       id[i][j]=cnt;
       if(islower(tomymaze[i][j])){
        s[tomymaze[i][j]-'a']=cnt;
    }
       if(isupper(tomymaze[i][j])){
        t[tomymaze[i][j]-'A']=cnt;
    }
cnt++;
        }
        }
}
for(int i=0;i<cnt;i++){
    degree[i]=0;
    for(int j=0; j<5; j++){
    int nx=x[i]+dx[j],ny=y[i]+dy[j];
    if(tomymaze[nx][ny]!='#'){
    mymap[i][degree[i]++]=id[nx][ny];
    }
    }
}
if(k<=2){
    degree[cnt]=1;
    mymap[cnt][0]=cnt;
    s[2]=t[2]=cnt++;
}
if(k<=1){
    degree[cnt]=1;
    mymap[cnt][0]=cnt;
    s[1]=t[1]=cnt++;
}
printf("%d\n",bfs());
}
return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值