UVA - 1601 The Morning after Halloween (BFS/双向BFS/A*)
挺有意思但是代码巨恶心的一道最短路搜索题。
因为图中的结点太多,应当首先考虑把隐式图转化成显式图,即对地图中可以相互连通的点之间连边,建立一个新图(由于每步不需要每个鬼都移动,所以每个点需要向自己也连一条边)。设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*)的更多相关文章
- UVA - 1601 The Morning after Halloween (双向BFS&单向BFS)
题目: w*h(w,h≤16)网格上有n(n≤3)个小写字母(代表鬼).要求把它们分别移动到对应的大写字母里.每步可以有多个鬼同时移动(均为往上下左右4个方向之一移动),但每步结束之后任何两个鬼不能占 ...
- 【UVa】1601 The Morning after Halloween(双向bfs)
题目 题目 分析 双向bfs,对着书打的,我还调了好久. 代码 #include<cstdio> #include<cstring> #include<c ...
- UVa 1601 || POJ 3523 The Morning after Halloween (BFS || 双向BFS && 降维 && 状压)
题意 :w*h(w,h≤16)网格上有n(n≤3)个小写字母(代表鬼).要求把它们分别移动到对应的大写字母里.每步可以有多个鬼同时移动(均为往上下左右4个方向之一移动),但每步结束之后任何两个鬼不能占 ...
- UVA1601-The Morning after Halloween(双向BFS)
Problem UVA1601-The Morning after Halloween Accept: 289 Submit: 3136 Time Limit: 12000 mSec Problem ...
- POJ1915Knight Moves(单向BFS + 双向BFS)
题目链接 单向bfs就是水题 #include <iostream> #include <cstring> #include <cstdio> #include & ...
- POJ 3126 Prime Path 解题报告(BFS & 双向BFS)
题目大意:给定一个4位素数,一个目标4位素数.每次变换一位,保证变换后依然是素数,求变换到目标素数的最小步数. 解题报告:直接用最短路. 枚举1000-10000所有素数,如果素数A交换一位可以得到素 ...
- UVA 1601 The Morning after Halloween
题意: 给出一个最大为16×16的迷宫图和至多3个ghost的起始位置和目标位置,求最少经过几轮移动可以使三个ghost都到达目标位置.每轮移动中,每个ghost可以走一步,也可以原地不动,需要注意的 ...
- POJ1915 BFS&双向BFS
俩月前写的普通BFS #include <cstdio> #include <iostream> #include <cstring> #include <q ...
- bfs(双向bfs加三维数组)
http://acm.hdu.edu.cn/showproblem.php?pid=2612 Find a way Time Limit: 3000/1000 MS (Java/Others) ...
随机推荐
- Spring笔记:事务管理
Spring笔记:事务管理 事务管理 Spring事务管理是通过SpringAOP去实现的.默认情况下Spring在执行方法抛出异常后,引发事务回顾,当然你可以用拦截器或者配置去改变它们. 这部门内容 ...
- pyhton3 sys模块
Python常用模块之sys sys模块提供了一系列有关Python运行环境的变量和函数. 1 ). sys.stdin 标准输入流.2 ).sys.stdout 标准输出流.3 ). sys.std ...
- c# 接口(interface)与接口应用
using System; using System.Collections.Generic; using System.Linq; using System.Text; //接口(interface ...
- 使用CoreData存储数据
- (void)viewDidLoad { [super viewDidLoad]; //获取模型文件的路径 NSString *path=[[NSBundle mainBundle]pathForR ...
- hadoop06---多线程
.1.1. 实现线程的两种方式 1.继承Thread的方式 见代码MyThreadWithExtends 2.声明实现 Runnable 接口的方式 见代码MyThreadWithImpliment ...
- I.mx6s上移植wm8960驱动(基于linux3.0.101版本)
I.mx6s上移植wm8960驱动 此篇博文只记录移植的步骤,其他不做分析.首先上一张wm8960的硬件连接图: 1 上电操作 配置wm8960的上电脚,文件位置:arch/arm/mach ...
- freemarker内建函数介绍
Sequence的内置函数1.sequence?first 返回sequence的第一个值.2.sequence?last 返回sequence的最后一个值.3.sequence?reverse 将s ...
- Linux 基本命令___0002
来源:https://mp.weixin.qq.com/s/DmfpDfWpWRV3EDItDdYgXQ #配置vim #http://www.cnblogs.com/ma6174/archive/2 ...
- iOS Font
1. 非常棒的查看字体样貌网站: http://iosfonts.com 1. 查看设备支持字体: NSArray *familyNames = [NSMutableArray arrayWithAr ...
- svn working copy locked的解决方法
在使用svn更新或提交代码时,会报"svn working copy XXX locked"的错误,利用svn客户端工具TortoiseSVN的cleanup也不能解决问题. 我们 ...