题目链接

挺有意思但是代码巨恶心的一道最短路搜索题。

因为图中的结点太多,应当首先考虑把隐式图转化成显式图,即对地图中可以相互连通的点之间连边,建立一个新图(由于每步不需要每个鬼都移动,所以每个点需要向自己也连一条边)。设d[i][j][k]为走到“A在结点i,B在结点j,C在结点k”的状态需要多少步,直接bfs即可。

注意由于鬼的个数不确定,为了减少特判,需要留出三个虚节点,把多出来的鬼的起点和终点都设到同一个虚节点上。

(代码刚写完后发现样例的答案比正确的少了2,检查了好久才发现自己建图的时候tot多加了1...)

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+;
struct D {int a[];};
struct E {int v,nxt;} e[];
int rt[N][N],d[][][],n,m,k,tot,ne,hd[],bg[],ed[];
char s[N][N];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
bool ok(int* u,int* v) {
if(v[]==v[]||v[]==v[]||v[]==v[])return ;
if(u[]==v[]&&u[]==v[])return ;
if(u[]==v[]&&u[]==v[])return ;
if(u[]==v[]&&u[]==v[])return ;
return ;
} int bfs() {
int u[],v[];
queue<D> q;
q.push({bg[],bg[],bg[]}),d[bg[]][bg[]][bg[]]=;
while(!q.empty()) {
memcpy(u,q.front().a,sizeof u),q.pop();
if(u[]==ed[]&&u[]==ed[]&&u[]==ed[])return d[u[]][u[]][u[]];
for(int i=hd[u[]]; ~i; i=e[i].nxt)
for(int j=hd[u[]]; ~j; j=e[j].nxt)
for(int k=hd[u[]]; ~k; k=e[k].nxt) {
v[]=e[i].v,v[]=e[j].v,v[]=e[k].v;
if(ok(u,v)&&!~d[v[]][v[]][v[]]) {
d[v[]][v[]][v[]]=d[u[]][u[]][u[]]+;
q.push({v[],v[],v[]});
}
}
}
return -;
} int main() {
while(scanf("%d%d%d",&m,&n,&k)&&n) {
scanf(" ");
memset(hd,-,sizeof hd),ne=,tot=;
for(int i=; i<; ++i)bg[i]=ed[i]=i;
addedge(,),addedge(,),addedge(,);
for(int i=; i<n; ++i)gets(s[i]);
for(int i=; i<n-; ++i)for(int j=; j<m-; ++j)if(s[i][j]!='#') {
rt[i][j]=tot++;
addedge(rt[i][j],rt[i][j]);
if(s[i-][j]!='#') {
addedge(rt[i][j],rt[i-][j]);
addedge(rt[i-][j],rt[i][j]);
}
if(s[i][j-]!='#') {
addedge(rt[i][j],rt[i][j-]);
addedge(rt[i][j-],rt[i][j]);
}
if(isupper(s[i][j]))bg[s[i][j]-'A']=rt[i][j];
else if(islower(s[i][j]))ed[s[i][j]-'a']=rt[i][j];
}
for(int i=; i<tot; ++i)for(int j=; j<tot; ++j)for(int k=; k<tot; ++k)d[i][j][k]=-;
printf("%d\n",bfs());
}
return ;
}

bfs

这个代码跑了1000+ms,我们可以继续优化。

优化一:由于把一步移动撤回的规则和正向移动的规则是一样的,因此可以把bfs改成双向的,即6个鬼同时从起点和终点出发直到相遇,这样可以降低bfs树的深度,少扩展一些结点。方法是将d数组多开一维,代表每个状态是正向转移来的还是反向转移来的。如果一个状态的反状态(对应鬼的位置均相等,bfs方向相反),则两状态的d值相加即为最短距离。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+;
struct D {int f,a[];};
struct E {int v,nxt;} e[];
int rt[N][N],d[][][][],n,m,k,tot,ne,hd[],bg[],ed[];
char s[N][N];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
bool ok(int* u,int* v) {
if(v[]==v[]||v[]==v[]||v[]==v[])return ;
if(u[]==v[]&&u[]==v[])return ;
if(u[]==v[]&&u[]==v[])return ;
if(u[]==v[]&&u[]==v[])return ;
return ;
} int bfs() {
int u[],v[],f;
queue<D> q;
q.push({,bg[],bg[],bg[]}),d[][bg[]][bg[]][bg[]]=;
q.push({,ed[],ed[],ed[]}),d[][ed[]][ed[]][ed[]]=;
while(!q.empty()) {
memcpy(u,q.front().a,sizeof u),f=q.front().f,q.pop();
if(~d[f^][u[]][u[]][u[]])return d[f][u[]][u[]][u[]]+d[f^][u[]][u[]][u[]];
for(int i=hd[u[]]; ~i; i=e[i].nxt)
for(int j=hd[u[]]; ~j; j=e[j].nxt)
for(int k=hd[u[]]; ~k; k=e[k].nxt) {
v[]=e[i].v,v[]=e[j].v,v[]=e[k].v;
if(ok(u,v)&&!~d[f][v[]][v[]][v[]]) {
d[f][v[]][v[]][v[]]=d[f][u[]][u[]][u[]]+;
q.push({f,v[],v[],v[]});
}
}
}
return -;
} int main() {
while(scanf("%d%d%d",&m,&n,&k)&&n) {
scanf(" ");
memset(hd,-,sizeof hd),ne=,tot=;
for(int i=; i<; ++i)bg[i]=ed[i]=i;
addedge(,),addedge(,),addedge(,);
for(int i=; i<n; ++i)gets(s[i]);
for(int i=; i<n-; ++i)for(int j=; j<m-; ++j)if(s[i][j]!='#') {
rt[i][j]=tot++;
addedge(rt[i][j],rt[i][j]);
if(s[i-][j]!='#') {
addedge(rt[i][j],rt[i-][j]);
addedge(rt[i-][j],rt[i][j]);
}
if(s[i][j-]!='#') {
addedge(rt[i][j],rt[i][j-]);
addedge(rt[i][j-],rt[i][j]);
}
if(isupper(s[i][j]))bg[s[i][j]-'A']=rt[i][j];
else if(islower(s[i][j]))ed[s[i][j]-'a']=rt[i][j];
}
for(int i=; i<tot; ++i)for(int j=; j<tot; ++j)for(int k=; k<tot; ++k)d[][i][j][k]=d[][i][j][k]=-;
printf("%d\n",bfs());
}
return ;
}

bfs(双向)

跑了600+ms,感觉也没快多少~~

优化二:可以考虑用A*算法,新开一个h数组记录每个节点分别到a,b,c结点的最短距离(可用bfs预处理),则当前状态(i,j,k)到(a,b,c)的最短距离不超过f[i][j][k]=d[i][j][k]+max(h[a][i],h[b][j],h[c][k]),选择把原来的队列换成优先队列,每次取出f值最小的结点进行扩展即可。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+;
struct E {int v,nxt;} e[];
int rt[N][N],d[][][],h[][],n,m,k,tot,ne,hd[],bg[],ed[];
char s[N][N];
struct D {
int a[];
bool operator<(const D& b)const {
return d[a[]][a[]][a[]]+max(h[][a[]],max(h[][a[]],h[][a[]]))
>d[b.a[]][b.a[]][b.a[]]+max(h[][b.a[]],max(h[][b.a[]],h[][b.a[]]));
}
};
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
bool ok(int* u,int* v) {
if(v[]==v[]||v[]==v[]||v[]==v[])return ;
if(u[]==v[]&&u[]==v[])return ;
if(u[]==v[]&&u[]==v[])return ;
if(u[]==v[]&&u[]==v[])return ;
return ;
} void bfs(int S,int* d) {
int u,v;
queue<int> q;
for(int i=; i<tot; ++i)d[i]=-;
q.push(S),d[S]=;
while(!q.empty()) {
u=q.front(),q.pop();
for(int i=hd[u]; ~i; i=e[i].nxt) {
v=e[i].v;
if(!~d[v])d[v]=d[u]+,q.push(v);
}
}
} int Astar() {
int u[],v[];
priority_queue<D> q;
d[bg[]][bg[]][bg[]]=,q.push({bg[],bg[],bg[]});
while(!q.empty()) {
memcpy(u,q.top().a,sizeof u),q.pop();
if(u[]==ed[]&&u[]==ed[]&&u[]==ed[])return d[u[]][u[]][u[]];
for(int i=hd[u[]]; ~i; i=e[i].nxt)
for(int j=hd[u[]]; ~j; j=e[j].nxt)
for(int k=hd[u[]]; ~k; k=e[k].nxt) {
v[]=e[i].v,v[]=e[j].v,v[]=e[k].v;
if(ok(u,v)&&!~d[v[]][v[]][v[]]) {
d[v[]][v[]][v[]]=d[u[]][u[]][u[]]+;
q.push({v[],v[],v[]});
}
}
}
return -;
} int main() {
while(scanf("%d%d%d",&m,&n,&k)&&n) {
scanf(" ");
memset(hd,-,sizeof hd),ne=,tot=;
for(int i=; i<; ++i)bg[i]=ed[i]=i;
addedge(,),addedge(,),addedge(,);
for(int i=; i<n; ++i)gets(s[i]);
for(int i=; i<n-; ++i)for(int j=; j<m-; ++j)if(s[i][j]!='#') {
rt[i][j]=tot++;
addedge(rt[i][j],rt[i][j]);
if(s[i-][j]!='#') {
addedge(rt[i][j],rt[i-][j]);
addedge(rt[i-][j],rt[i][j]);
}
if(s[i][j-]!='#') {
addedge(rt[i][j],rt[i][j-]);
addedge(rt[i][j-],rt[i][j]);
}
if(isupper(s[i][j]))bg[s[i][j]-'A']=rt[i][j];
else if(islower(s[i][j]))ed[s[i][j]-'a']=rt[i][j];
}
for(int i=; i<tot; ++i)for(int j=; j<tot; ++j)for(int k=; k<tot; ++k)d[i][j][k]=-;
for(int i=; i<; ++i)bfs(ed[i],h[i]);
printf("%d\n",Astar());
}
return ;
}

A*

我用A*算法跑样例的速度明显快了好几个档次,但提交上去却依旧跑了400+ms,看来A*终究逃不过被卡的命运~~

UVA - 1601 The Morning after Halloween (BFS/双向BFS/A*)的更多相关文章

  1. UVA - 1601 The Morning after Halloween (双向BFS&单向BFS)

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

  2. 【UVa】1601 The Morning after Halloween(双向bfs)

    题目 题目     分析 双向bfs,对着书打的,我还调了好久.     代码 #include<cstdio> #include<cstring> #include<c ...

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

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

  4. UVA1601-The Morning after Halloween(双向BFS)

    Problem UVA1601-The Morning after Halloween Accept: 289 Submit: 3136 Time Limit: 12000 mSec  Problem ...

  5. POJ1915Knight Moves(单向BFS + 双向BFS)

    题目链接 单向bfs就是水题 #include <iostream> #include <cstring> #include <cstdio> #include & ...

  6. POJ 3126 Prime Path 解题报告(BFS & 双向BFS)

    题目大意:给定一个4位素数,一个目标4位素数.每次变换一位,保证变换后依然是素数,求变换到目标素数的最小步数. 解题报告:直接用最短路. 枚举1000-10000所有素数,如果素数A交换一位可以得到素 ...

  7. UVA 1601 The Morning after Halloween

    题意: 给出一个最大为16×16的迷宫图和至多3个ghost的起始位置和目标位置,求最少经过几轮移动可以使三个ghost都到达目标位置.每轮移动中,每个ghost可以走一步,也可以原地不动,需要注意的 ...

  8. POJ1915 BFS&双向BFS

    俩月前写的普通BFS #include <cstdio> #include <iostream> #include <cstring> #include <q ...

  9. bfs(双向bfs加三维数组)

    http://acm.hdu.edu.cn/showproblem.php?pid=2612 Find a way Time Limit: 3000/1000 MS (Java/Others)     ...

随机推荐

  1. 浅谈HTTPS协议

    前言 理解协议是做接口测试的前提.本文主要向大家展示博主对HTTPS协议的理解,网上有诸多资料,有些写得过于晦涩难懂,尤其是需要密码学的一些知识.我做了一下简单的整理,刨除复杂的底层实现,单从理解SS ...

  2. iOS 认识runtime 中的三个指针 isa , IMP , SEL

    runtime中函数调用经常被提及的三个概念 isa,IMP,SEL 一  isa:是类指针,之所以说isa是指针是因为Class其实是一个指向objc_class结构体的指针,而isa 是它唯一的私 ...

  3. Oracle索引(1)概述与创建索引

    索引是为了提高数据检索效率而创建的一种独立于表的存储结构,由Oracle系统自动进行维护. 索引的概述        索引是一种可选的与表或簇相关的数据库对象,能够为数据的查询提供快捷的存储路径,减少 ...

  4. 方法——<37>

    1,返回url参数 /* * 返回参数值 * @method getUrlPara * @papram {string},url中参数名 * @return {string},url中参数值 * */ ...

  5. 【leetcode刷题笔记】Surrounded Regions

    Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. A region is captured ...

  6. BFC与边距重叠详解

    1.什么是BFC? 在解释 BFC 是什么之前,需要先介绍 Box.Formatting Context的概念. Box: CSS布局的基本单位Box 是 CSS 布局的对象和基本单位, 直观点来说, ...

  7. samtools使用过程中出现的问题

    1.EOP marker is absent 在使用samtools index时出现 EOF是指the end of file,即samtools认为你的bam文件是不完整的. 如果把view参数的 ...

  8. php数组函数-array_push()

    array_push()函数将一个或多个元素插入数组的末尾(入栈). 提示:可以添加一个或者多个值. 注:即使您的数组有字符串键名,您所添加的元素将是数字键名. array_push(array,va ...

  9. Spark-运行时架构

    Spark运行时架构 在分布式环境下,Spark集群采用的时主/从结构.在一个Spark集群中,有一个节点负责中央协调,调度各个分布式工作节点.这个中央协调节点被称为驱动器(Driver),与之对应的 ...

  10. QT 利用QSplitter 分割区域, 并添加QScrollArea 滚动区域,滚动条

    1. QSplitter 分割区域, 可以分割区域中可以随意添加自己的布局 2. #include "dialog.h" #include <QApplication> ...