题目大意

  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. Ubuntu-Python2.7安装 scipy,numpy,matplotlib

    sudo apt-get install python-scipy sudo apt-get install python-numpy sudo apt-get install python-matp ...

  2. Jmeter在Windows上分布式压测遇到的坑

    1.五星坑:远程启动测试,响应数据为空. controller运行jmeter脚本后,GUI无性能数据返回. agent的jmeter server显示连接后立即结束.看似执行实则响应数据为空. 出现 ...

  3. Java基础概念语法

    Java基础概念语法 注释 单行注释 //行注释说明 多行注释 /* 多行注释说明 */ 文档注释 /** *@author 程序的作者 *@version 源文件的版本 *@param 方法的参数说 ...

  4. Gitlab forbidden

    搭建使用了两年的gitlab 抽风了居然forbidden # vim /etc/gitlab/gitlab.rb gitlab_rails['rack_attack_git_basic_auth'] ...

  5. <东方梦符祭> N1无尽30波终于通了

    嘛也算是第一次通关 纪念一下 阵容:xjb搭的杂牌队 主C:古明绝恋 露米娅(真·R卡战神)比那名居天子 斯卡雷特 控制:琪露诺 蕾蒂 灵梦 挂件:小伞 纳兹琳 古明地觉 永江依玖 第一发就直接抽到了 ...

  6. oracle中的冷热备份

    oracle有四种备份方法:冷备份.热备份.RMAN备份.逻辑备份. 其中冷备份和热备份都是用操作系统命令对oracle文件直接进行拷贝, 不同的是冷备份是把数据库关闭后再备份,备份过程中也要关闭数据 ...

  7. Error LNK2019: unresolved external symbol C++模板类声明与定义链接错误问题

    编译器在编译模板时,并不会生成代码,只有遇到实例化的时候才会生成代码.因此,当我们只引用模板声明文件的时候,在实例化的对象时候,模板的定义问文件是不可见的,于是出现链接错误.例如: //A.h #pr ...

  8. Qt 如何处理密集型耗时的事情

    有时候需要处理一些跟界面无关的但非常耗时的事情,这些事情跟界面在同一个线程中,由于时间太长,导致界面无法响应,处于“假死”状态.例如:在应用程序中保存文件到硬盘上,从开始保存直到文件保存完毕,程序不响 ...

  9. 洛谷——P2734 游戏 A Game

    P2734 游戏 A Game 题目背景 有如下一个双人游戏:N(2 <= N <= 100)个正整数的序列放在一个游戏平台上,游戏由玩家1开始,两人轮流从序列的任意一端取一个数,取数后该 ...

  10. Python使用Flask框架,结合Highchart,搭配数据功能模块,加载 HTML 表格数据

    参考链接:https://www.highcharts.com.cn/docs/data-modules 1.javascript代码 var chart = Highcharts.chart('co ...