题目链接

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

因为图中的结点太多,应当首先考虑把隐式图转化成显式图,即对地图中可以相互连通的点之间连边,建立一个新图(由于每步不需要每个鬼都移动,所以每个点需要向自己也连一条边)。设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. 前端基础之JavaScript_(4)_js的作用域

    js作用域 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变量的作用域有全局作用域和局部作用域两种. js的作用域 1. 全局作用域(Glob ...

  2. Linux系统通过console口连接交换机

    一.安装minicomUbuntu安装:sudo apt-get install minicom.centos安装:yum install minicom二.配置minicomUbuntu输入:sud ...

  3. 第二篇 Python图片处理模块PIL(pillow)

    本篇包含:16.Point    17.Putalpha    18.Putdata    19.Putpalette    20.Putpixel      21.Quantize     22.R ...

  4. PAT 天梯赛 L1-030. 一帮一 【STL】

    题目链接 https://www.patest.cn/contests/gplt/L1-030 思路 用三个 Vector 来分别存放 整个排名,以及男生的单独排名和女生的单独排名 从整个的排名 从上 ...

  5. 服务中的 API 网关(API Gateway)

    我们知道在微服务架构风格中,一个大应用被拆分成为了多个小的服务系统提供出来,这些小的系统他们可以自成体系,也就是说这些小系统可以拥有自己的数据库,框架甚至语言等,这些小系统通常以提供 Rest Api ...

  6. Sublime text3装入插件Anaconda

    好多人在学习编写代码的时候,会有不同的编辑器,大神们建议我使sublime,但是我在里面写代码,没有一点提示,我的天,不得烧坏我的脑子啊.学Python我用的Ananconda,但是这个spyder有 ...

  7. 20145231 《Java程序设计》第一周学习总结

    20145231 <Java程序设计>第一周学习总结 教材学习内容总结 Java三大平台Java SE,Java EE,Java ME.其中,Java SE是我们学习的基础. Java S ...

  8. INSPIRED启示录 读书笔记 - 第20章 基本产品

    消减功能还是延长工期 不要再试图定义最终产品,转而定义只满足基本要求的产品,简称基本产品 1.产品经理与设计师合作设计产品的高保真原型,这个原型只具备实现商业目标的最基本功能要求,以及良好的用户体验和 ...

  9. 解决Newtonsoft.Json版本问题

    在配置文件中添加以下代码,App.config或Web.config <runtime> <assemblyBinding xmlns="urn:schemas-micro ...

  10. 深入剖析JDK动态代理源码实现

    动态代理.静态代理优缺点优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性.这是代理的共有优点.动态代理只有在用到被代理对象的时候才会对被代理类进行类加载. 而静态代理在编译器就已经开始占内存了 ...