[Poj3523][Uva1601][Aizu1281] The Morning after Halloween 【A*算法】

题目描述:

某节后的早晨blabla……不知怎的在一个w*h的地图中出现了k个小鬼(用a,b,c标识),他们要走到各自的目标点(用A,B,C标识),在接下来的每个单位时间中,三个小鬼可以选择不移动或者朝四周移动一步,选择是相互独立的,只是:不能相撞,不能同时站在一个格子里,求最小的单位时间。(4 ≤ w ≤ 16, 4 ≤ h ≤ 16, 1 ≤ n ≤ 3)

样例输入:

5 5 2
#####
#A#B#
#   #
#b#a#
#####
16 4 3
################
## ########## ##
#    ABCcba    #
################ 

0 0 0

样例输出:

7
36



卤煮番外(xianche)(明天回老家今晚浪一浪~):

③写次双广惨烈MLE,在卤煮换了unsignedshort,unsignedchar后还是因为写得丑MLE后,丢掉又写了普通A*惨烈TLE,saogao一会后又换了个h函数惨烈WA了十几次又开始手写(saogao)数据后来才发现™h函数不小心写错了啊!!!



正经

思路:

①双广:比较裸吧,找状态时三层循环枚举abc是向上/下/左/右走还是不走,就是容易T和M。

②A*:找状态同上,就是估值函数,我开始写的是曼哈顿距离,后来第三个样例(77那个,这里格式问题我懒得粘了)跑了将近2s才出结果,后来多写了个普通BFS预处理出图中每个点到A/B/C点的距离,用dis[(0/1/2)][i][j](表示坐标(i,j)到A/B/C点的距离)存储,h函数设置为max(dis[0/1/2][i][j]),即 使所有点归位所需要的最少步数。改后样例秒出,poj跑了2000+ms。




代码:

#include<iostream>  
#include<cstring>  
#include<cstdio>  
#include<queue>  
#define USI short int  
  
const int maxn = 16 ;  
using namespace std; 
  
struct prenode  //普通bfs用
{  
    USI X,Y;  
    prenode(USI a,USI b){  
        X = a, Y = b;  
    }  
};  
  
struct node //A*用
{  
    USI ax, ay, bx, by, cx, cy, step, pre;  //step:步数g,pre:估值函数h
    node(USI a, USI b, USI c, USI d, USI e, USI f, USI g, USI h){  
        ax = a, ay = b, bx = c, by = d, cx = e, cy = f, step = g, pre = h;  
    }  
    bool operator < (const node &a)const{  
        return step + pre > a.step + a.pre;  
    }  
};  
  
int n, m, K;  
USI edax, eday, edbx, edby, edcx, edcy, stax, stay, stbx, stby, stcx, stcy;  
USI Move[5][2] = {{0, 0},{1, 0}, {-1, 0}, {0, -1}, {0, 1}};  //前面一定要有00,因为可以不走
char map[maxn + 5][maxn + 5], x;  
bool oc[5];  //occur
USI f[maxn+1][maxn+1][maxn+1][maxn+1][maxn+1][maxn+1];  //保存启发函数
USI dis[3][maxn+1][maxn+1];  //预处理的每个点到ABC三点的最短距离
  
void init()  
{  
    stax = stay = stbx = stby = stcx = stcy = 0;  
    edax = eday = edbx = edby = edcx = edcy = 0;  
    memset(map, 0, sizeof map);  
    memset(oc, 0, sizeof oc);  
    memset(f, 4, sizeof f);  
    memset(dis, 4, sizeof dis);  
}  
  
void read()  //开始WA的几次或许也有读入的问题,后来参考了一下其他人的代码,总之比较坑吧
{  
    gets(map[0]);  
    for(int i = 0; i < n; i ++)  
        gets(map[i]);  
    for(int i = 0; i < n; i ++){  
        for(int j = 1; j < m; j ++){  
            if(map[i][j] == 'A') edax = i,  eday= j, oc[0] = 1;  
            else if(map[i][j] == 'B') edbx = i, edby = j, oc[1] = 1;  
            else if(map[i][j] == 'C') edcx = i, edcy = j, oc[2] = 1;  
      
            else if(map[i][j] == 'a') stax = i, stay = j;  
            else if(map[i][j] == 'b') stbx = i, stby = j;  
            else if(map[i][j] == 'c') stcx = i, stcy = j;  
        }  
    }  
}  
  
bool checkbound(int xx, int yy)  //是否为墙/在边界内
{  
    return map[xx][yy] != '#' && xx > 0 && yy > 0 && xx <= n && yy <= m ;  
}  
  
  
void pre_h(int op)  //预处理h函数(距离)
{  
    queue<prenode> Q;  
    char des = 'A' + op;  
    USI stx, sty;  
    if(des == 'A')stx = edax, sty = eday;  
    else if(des == 'B')stx = edbx, sty = edby;  
    else if(des == 'C') stx = edcx , sty = edcy;   
    Q.push(prenode(stx, sty));  
    dis[op][stx][sty] = 0;  
    while(!Q.empty()){  
        prenode now = Q.front();  
        Q.pop();  
        for(int i = 1; i <= 4; i ++){  
            USI tx = now.X + Move[i][0], ty = now.Y + Move[i][1];  
            if(!checkbound(tx, ty))continue;  
            if(dis[op][tx][ty] > dis[op][now.X][now.Y] + 1){  
                dis[op][tx][ty] = dis[op][now.X][now.Y] + 1;  
                Q.push(prenode(tx, ty));  
            }  
        }  
    }  
}  
  
USI h(USI Ax, USI Ay, USI Bx, USI By, USI Cx, USI Cy)  
{  
    return max(dis[0][Ax][Ay] ,max( dis[1][Bx][By] , dis[2][Cx][Cy]));  //开始手贱打成dis[0][][]+dis[1][][]+dis[2][][]WA了好多次太傻了
}  
  
void Astar()  
{  
    priority_queue<node> myque;  
    myque.push(node(stax, stay, stbx, stby, stcx, stcy, 0, h(stax,stay,stbx,stby,stcx,stcy)));  
    f[stax][stay][stbx][stby][stcx][stcy] = h(stax,stay,stbx,stby,stcx,stcy);  
    USI tax = 0, tay = 0, tbx = 0, tby = 0, tcx = 0, tcy = 0;  
	
    while(!myque.empty()){  
        node cur = myque.top();  
        myque.pop();  
		
        tax = 0, tay = 0, tbx = 0, tby = 0, tcx = 0, tcy = 0;  

        if(cur.step + cur.pre> f[cur.ax][cur.ay][cur.bx][cur.by][cur.cx][cur.cy])continue;  
		
        if(cur.ax == edax && cur.ay == eday && cur.bx == edbx && cur.by == edby && cur.cx == edcx && cur.cy == edcy)  
        {
            printf("%d\n", cur.step);  
            return ;  
        } //结束条件:是否都到达了目的地
		
        for(int i = 0; i <= 4; i ++){  //枚举a的移动
            if(oc[0]){  //如果a/A存在,下面的if(oc[])同
                tax = cur.ax + Move[i][0], tay = cur.ay + Move[i][1];  
                if(!checkbound(tax, tay))continue;//各种判判判,下面带continue的同  
            }  
            for(int j = 0; j <= 4; j ++){  //枚举b的移动
                if(oc[1] != 0){  
                    tbx = cur.bx + Move[j][0], tby = cur.by + Move[j][1];  
                    if(!checkbound(tbx, tby))continue;  
                    if(tax == tbx && tay == tby)continue;  
                    if(tax == cur.bx && tbx == cur.ax && tay == tby)continue;  
                    if(tay == cur.by && tby == cur.ay && tax == tbx)continue;  
                }  
                for(int k = 0; k <= 4; k ++){  //枚举c的移动
                    if(oc[2] != 0){  
                        tcx = cur.cx + Move[k][0], tcy = cur.cy + Move[k][1];  
                        if(!checkbound(tcx, tcy))continue;  
                        if((tcx == tbx && tcy == tby) || (tcx == tax && tcy == tay))continue;  
                        if(tcx == cur.bx && tbx == cur.cx && tby == tcy)continue;  
                        if(tcy == cur.by && tby == cur.cy && tbx == tcx)continue;  
                        if(tcx == cur.ax && tax == cur.cx && tay == tcy)continue;  
                        if(tcy == cur.ay && tay == cur.cy && tcx == tax)continue;  
                    }
					
                    USI t = h(tax, tay, tbx, tby, tcx, tcy);  
                    if(cur.step + 1 + t < f[tax][tay][tbx][tby][tcx][tcy]){  
                        f[tax][tay][tbx][tby][tcx][tcy] = cur.step + 1+ t;  
                        myque.push(node(tax,tay,tbx,tby,tcx,tcy,cur.step+1,t));//判完辣进队  
                    }  
                }  
            }  
        }  
    }  
    return;  
}  
  
void debug()  
{  
    for(int i = 0; i < n; i ++){  
        for(int j = 0; j < m; j ++){  
            if(dis[0][i][j] == 1028){printf("-- ");continue;}  
            if(dis[0][i][j] < 10) printf(" ");  
            printf("%d ",dis[0][i][j]);  
        }  
        puts("");  
    }  
    puts("");  
    for(int i = 0; i < n; i ++){  
        for(int j = 0; j < m; j ++){  
            if(dis[1][i][j] == 1028){printf("-- ");continue;}  
            if(dis[1][i][j] < 10) printf(" ");  
            printf("%d ",dis[1][i][j]);  
        }  
        puts("");  
    }  
    puts("");  
    for(int i = 0; i < n; i ++){  
        for(int j = 0; j < m; j ++){  
            if(dis[2][i][j] == 1028){printf("-- ");continue;}  
            if(dis[2][i][j] < 10) printf(" ");  
            printf("%d ",dis[2][i][j]);  
        }  
        puts("");  
    }  
    puts("");  
}  
  
int main()  
{  
    while(scanf("%d%d%d", &m, &n, &K) != EOF && n && m && K){  
        init();  
        read();  
        for(int i = 0; i < 3; i ++){  
            if(oc[i])  
                pre_h(i);  
            else   
                memset(dis[i],0,sizeof dis[i]);  
        }  
        //debug();  
        Astar();  
    }  
}  
/* 
 
16 16 3 
################ 
#              # 
# ############ # 
# #          # # 
# # ######## # # 
# # #      # # # 
# # # #### # # # 
# # # #AC# # # # 
# # # # B# # # # 
# # # # ## # # # 
# # # #    # # # 
# # # ###### # # 
#c# #        # # 
#b# ######## # # 
#a             # 
################ 传说中让我觉醒的数据。。。。
 
*/  


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值