题目描述:
某节后的早晨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 #
################ 传说中让我觉醒的数据。。。。
*/