题目大意

  w h (w, h <= 16)的网格有 n ( n <= 3) 个小写字母(代表鬼)其余的是‘#’(代表障碍格) 或 ‘ ’(代表空格。 要求把他们移动到对应的大写字母里。每步可以有多个鬼同时移动(均为上下左右4个移动方向之一), 但每步移动两个鬼不能占用同一个位置, 也不能在一步之内交换位置。输入保证空格联通,障碍联通,且在2 2子网格中至少有一个障碍格,并且最外面一层是障碍格。输入保证有解。

基本原理

  Bfs模拟三个小鬼到处乱走的状态即可。

优化

  1. 我们可以将带障碍的网格图更改为一张由节点和边组成的图,避免了搜索时对障碍的判断。
  2. 对于状态的判重,不要用stl的set$O\log n$解决,我们要想办法在$O(1)$的时间内判重。建图时图的节点的下标要离散化,不是其在网格图中的位置row * TotCol + col,而是新建出这个节点的序号。这样,由于位置最多有$(2^4)^2=2^8$,所以我们可以如此状态压缩:将第一个小人所在节点的编号|(第二个小人所在节点的编号<<8)|(第三个小人所在节点的编号<<16)。这样我们就可以用数组来判重了。
  3. 循环总是比递归快。进行状态转移时,不要递归每一个小人的位置。用循环枚举。对于人数<3的情况,将不用的人放到一个孤立点中即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <iostream>
#include <bitset>
#include <cstdarg>
using namespace std; const int MAX_ROW = 20, MAX_COL = 16, MAX_ROLE = 3, MAX_EDGE = MAX_ROW * MAX_COL * 4, MAX_STATE = 20000000;
bool IsWall[MAX_ROW][MAX_COL];
int TotRole, TotRow, TotCol;
int Ans;
int Dist1[MAX_STATE], Dist2[MAX_STATE]; struct Node;
struct Edge; struct Node
{
Edge *Head;
}_nodes[MAX_ROW * MAX_COL];
int _vCount; Node *_cord[MAX_ROW][MAX_COL];
Node *NewNode(int row, int col)
{
return _cord[row][col] = _nodes + _vCount++;
} struct Edge
{
Node *To;
Edge *Next;
}_edges[MAX_EDGE];
int _eCount; void AddEdge(Node *from, Node *to)
{
Edge *e = _edges + ++_eCount;
e->To = to;
e->Next = from->Head;
from->Head = e;
} struct State
{
Node *Pos[MAX_ROLE];
int Dist; void Clear()
{
memset(Pos, NULL, sizeof(Pos));
Dist = 0;
} State() { Clear(); } bool CanMove(Node **tos)
{
for (int i = 0; i < MAX_ROLE; i++)
for (int j = i + 1; j < MAX_ROLE; j++)
{
if (tos[i] == Pos[j] && tos[j] == Pos[i])
return false;
if (tos[i] == tos[j])
return false;
}
return true;
} State GetMove(Node **tos)
{
State ans;
for (int i = 0; i < MAX_ROLE; i++)
ans.Pos[i] = tos[i];
return ans;
}
}Start, Target;
queue<State> q1, q2; void ClearAll()
{
memset(Dist1, -1, sizeof(Dist1));
memset(Dist2, -1, sizeof(Dist2));
while (!q1.empty())
q1.pop();
while (!q2.empty())
q2.pop();
Start.Clear();
Target.Clear();
memset(_nodes, 0, sizeof(_nodes));
memset(_edges, 0, sizeof(_edges));
memset(IsWall, 0, sizeof(IsWall));
_eCount = 0;
Ans = 0;
_vCount = 3;
Start.Pos[0] = _nodes, Start.Pos[1] = _nodes + 1, Start.Pos[2] = _nodes + 2;
Target.Pos[0] = _nodes, Target.Pos[1] = _nodes + 1, Target.Pos[2] = _nodes + 2;
memset(_cord, NULL, sizeof(_cord));
} void BuildGraph()
{
for (int row = 0; row < TotRow; row++)
for (int col = 0; col < TotCol; col++)
if (!IsWall[row][col] && !_cord[row][col])
NewNode(row, col);
for (int i = TotRole; i < MAX_ROLE; i++)
{
AddEdge(Start.Pos[i], Start.Pos[i]);
AddEdge(Target.Pos[i], Target.Pos[i]);
}
const int Dir[4][2] = { { 1, 0 },{ 0, 1 },{ -1, 0 },{ 0, -1 } };
for (int row = 0; row < TotRow; row++)
for (int col = 0; col < TotCol; col++)
{
if (IsWall[row][col])
continue;
AddEdge(_cord[row][col], _cord[row][col]);
for (int i = 0; i < 4; i++)
{
int row1 = row + Dir[i][0], col1 = col + Dir[i][1];
if (row1 < 0 || row1 >= TotRow || col1 < 0 || col1 >= TotCol)
continue;
if (IsWall[row1][col1])
continue;
AddEdge(_cord[row][col], _cord[row1][col1]);
}
}
} int State_int(State& S)
{
int state = S.Pos[0] - _nodes | (S.Pos[1] - _nodes << 8) | (S.Pos[2] - _nodes << 16);
return state;
} void DoNext(State& cur,
int *distIn, int *distOut,
queue<State>& qIn)
{
Node *tos[MAX_ROLE];
for (Edge *e0 = cur.Pos[0]->Head; e0; e0 = e0->Next)
for (Edge *e1 = cur.Pos[1]->Head; e1; e1 = e1->Next)
for (Edge *e2 = cur.Pos[2]->Head; e2; e2 = e2->Next)
{
tos[0] = e0->To, tos[1] = e1->To, tos[2] = e2->To;
if (!cur.CanMove(tos))
continue;
State next = cur.GetMove(tos);
next.Dist = cur.Dist + 1;
int nextS = State_int(next);
if (distIn[nextS] >= 0)
continue;
distIn[nextS] = next.Dist;
if (distOut[nextS] >= 0)
{
Ans = next.Dist + distOut[nextS];
return;
}
qIn.push(next);
}
} int Bfs()
{
Start.Dist = Target.Dist = 0;
Dist1[State_int(Start)] = Dist2[State_int(Target)] = 0;
q1.push(Start);
q2.push(Target);
while (true)
{
int curDist = q1.front().Dist;
while (q1.front().Dist == curDist)
{
State cur = q1.front();
q1.pop();
DoNext(cur, Dist1, Dist2, q1);
if (Ans)
return Ans;
}
while (q2.front().Dist == curDist)
{
State cur = q2.front();
q2.pop();
DoNext(cur, Dist2, Dist1, q2);
if (Ans)
return Ans;
}
}
return Ans;
} int main()
{
char s[MAX_COL + 5];
while (scanf("%d%d%d\n", &TotCol, &TotRow, &TotRole) && (TotRow || TotCol || TotRole))
{
ClearAll();
for (int row = 0; row < TotRow; row++)
{
memset(s, 0, sizeof(s));
fgets(s, sizeof(s), stdin);
for (int col = 0; col < TotCol; col++)
{
char ch = s[col];
if (ch == '#')
IsWall[row][col] = true;
else if ('a' <= ch && ch <= 'c')
Start.Pos[ch - 'a'] = NewNode(row, col);
else if ('A' <= ch && ch <= 'C')
Target.Pos[ch - 'A'] = NewNode(row, col);
}
}
BuildGraph();
printf("%d\n", Bfs());
}
return 0;
}

  

UVA1601 The Morning afther Halloween的更多相关文章

  1. UVa1601 - The Morning after Halloween [单向bfs]

    解题思路: 1.注意到2*2方格中必有一个#,那么最多只有192条通道,可以将所有非‘#’的位置提取出来用邻接表的方式建图,通过bfs搜索目标位置. 2.将三个ghost的位置(a,b,c)作为状态量 ...

  2. UVa 1601 || POJ 3523 The Morning after Halloween (BFS || 双向BFS && 降维 && 状压)

    题意 :w*h(w,h≤16)网格上有n(n≤3)个小写字母(代表鬼).要求把它们分别移动到对应的大写字母里.每步可以有多个鬼同时移动(均为往上下左右4个方向之一移动),但每步结束之后任何两个鬼不能占 ...

  3. 7-9The Morning after Halloween uva1601

    这题可以用普通bfs来做  也可以用双向bfs来做(先欠着) 有点类似专题训练的一题   不过那题是找钥匙开门   不过都用了状态压缩 题意:  n,m(<=16) 的网络上有t(<=3) ...

  4. The Morning after Halloween uva1601

    这道题思路还是比较清晰的,建图加bfs或双向bfs,其实后者比前者少了将近一半的时间.. 建图可以把某一点所拥有邻接点长度(数目)记录在数组0这个位置,因为这道题使用vector会超时. #inclu ...

  5. 【例题 7-9 UVA-1601】The Morning after Halloween

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 对于没有出现的,当成0节点就好. 所以总是认为有3个人需要走到各自的终点. 将平面图转成点边图.这样比较好枚举. (二维变成一维,模 ...

  6. POJ 3370. Halloween treats 抽屉原理 / 鸽巢原理

    Halloween treats Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7644   Accepted: 2798 ...

  7. Lightoj 题目1422 - Halloween Costumes(区间DP)

    1422 - Halloween Costumes   PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 ...

  8. CSUFT 1004 This is Halloween: Saving Money

    1004: This is Halloween: Saving Money Time Limit: 1 Sec      Memory Limit: 128 MB Submit: 11      So ...

  9. [POJ 3370] Halloween treats

    Halloween treats Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7143   Accepted: 2641 ...

随机推荐

  1. canvas一周一练 -- canvas绘制中国银行标志(4)

    运行效果: <!DOCTYPE html> <html> <head> </head> <body> <canvas id=" ...

  2. (转) 淘淘商城系列——Redis五种数据类型介绍

    http://blog.csdn.net/yerenyuan_pku/article/details/72855562 Redis支持五种数据类型:string(字符串),hash(哈希),list( ...

  3. Java实现打包文件

    把文件打包到压缩包里 public void zip (String... files) throws IOException { //创建文件打包流对象 ZipOutputStream zos = ...

  4. Rest 参数(...)

    javascript 之Rest 参数(...) ES6 Rest参数 Rest就是为解决传入的参数数量不一定, rest parameter(Rest 参数) 本身就是数组,数组的相关的方法都可以用 ...

  5. Ubuntu下安装oh-my-zsh

    本文参考 https://www.jianshu.com/p/9a5c4cb0452d 此文已在ubuntu下确实安装成功,只不过懒得截图了,可以参照上述地址,我在他原基础上进行了一些更改. 安装Oh ...

  6. Python 开发面试题

    Python部分 将一个字符串逆序,不能使用反转函数 求从10到100中能被3或5整除的数的和 What is Python? What are the benefits of using Pytho ...

  7. PAT 1143 Lowest Common Ancestor

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  8. (远程调试)-idea

    远程调试 1.开启远程调试的端口 tomcat示例: catalina.bat jpda start

  9. 【Codeforces 711C】Coloring Trees

    [链接] 我是链接,点我呀:) [题意] 连续相同的数字分为一段 你可以改变其中0为1~m中的某个数字(改变成不同数字需要不同花费) 问你最后如果要求分成恰好k段的话,最少需要多少花费 [题解] dp ...

  10. Java基础学习总结(76)——Java异常深入学习研究

        异常机制是指当程序出现错误后,程序如何处理.具体来说,异常机制提供了程序退出的安全通道.当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器. 异常处理的流程     当程序中抛 ...