洛谷P1979 华容道
题目大意:自己看去...
题解:做了一下午...本蒟蒻立志要写全网最详细的题解。╭(╯^╰)╮
begin....
暴力70分。可以让空格子到处乱走,只要某个状态的指定格子到目标格子,那么此时的
步数就是最小的啦。
ps:一开始我写的bfs...写到一半不会记录状态..后来看题解,只需要记录空格子和指定棋子
的位置就可以啦。其他的棋子都不变。暴力代码后面会给出。
时间复杂度呢就是q次询问,加上空格子和指定格子的位置就是O(q(nm)^2)。
正解呢..emmm...组织一下语言...orz...
我们发现我们只在一张图上进行q次询问..每次都进行bfs,那也太浪费时间了 。
而且,贪心的想,要想让指定棋子到目标位置,一定要让空格子到指定棋子周围吧。所以在一堆
指定棋子和空格子的状态中,只有空格子在指定棋子四周的状态是有用的。所以我们把这些状态
给抽离出来。
有用状态:空格子在指定棋子的上下左右。
有用状态的后继状态:
(1)本来空格子在指定棋子上下左右的状态的后继可以是空格子还是在棋子的上下左右,
也就是空格子围着指定格子转。
这个步数用bfs计算。
(2)本来空格子在指定棋子上下左右的后继状态是空格子与指定棋子交换了位置。步数为1。
记录状态:我们知道了这些状态怎么记录呢?
就是给棋盘重新标号。不是n*m的棋盘从第一行到最后一行1.2.3.4.5...。而是对状态进行标号。
对于某个棋子,假设到它的标号为k,那么假设它为指定棋子的位置,当空格子在它上面时,这个
状态为k+1,下面时k+2..左边k+3,右边k+4,再下一个棋子,当空格子在它上面时k+5,下面时k+6..
先理解标号是怎样的,后面再详细讲怎样标号。
让状态连续:上面的事情干完我们得到的只是,如果当前格子是指定格子,空格子在它的上/下/左/右/
然后去它上/下/左/右的步数或空格子与它交换的步数。但是这些状态是不连续的。所以我们要用边把它连起来。
就形成图啦。你就会得到下面这么一坨东西。
当然这张图还没画完,不是之前说状态不连续么?我还没有画上空格子和指定棋子交换位置的线呢。
然后图就连通了...
然后跑spfa求最短路径...
只是还有一个问题,终点一定在图上了,但是起点可能与图隔绝。
首先终点状态是什么,就是空格子在tx,ty四周。所以一定在图上。
但是可能起点sx,sy周围没有空格子,所以先让空白格到指定格周围,然后我们就求出
空白格到指定格周围的最小步数,还是bfs。
好。现在理一下思路。
由于只有当空白格在指定格周围状态才有用,我们要抽离出有用状态。
对于指定棋子可能待得地方都抽离状态并计算出后继状态。0,1,2,3,表示空白格到指定格的方向,下面详细讲。
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(map[i][j]){
if(map[i-][j])bfs(i-,j,i,j,);
if(map[i][j+])bfs(i,j+,i,j,);
if(map[i+][j])bfs(i+,j,i,j,);
if(map[i][j-])bfs(i,j-,i,j,);
}
标号 0 1 2 3表示空格子在指定棋子的上右下左,这样方便给状态标号。
而且在写mx[4]={...},my[4]={...}也要对应上右下左。
也方便连接空格子和指定格子交换位置时的边,看下面代码。
构图。目的是让状态连续,让状态称为点,边权为状态转移的步数,就是上面的图上连的边。
pre_dis[x][y]表示空格子到(x,y)的最小步数。
void bfs(int ex,int ey,int sx,int sy,int d){
memset(pre_dis,-,sizeof(pre_dis));
pre_dis[ex][ey]=;pre_dis[sx][sy]=;
Node now,nxt;now.x=ex;now.y=ey;
while(!q.empty())q.pop();
q.push(now);
while(!q.empty()){
now=q.front();q.pop();
int x=now.x,y=now.y;
for(int i=;i<;i++){
int xx=x+mx[i],yy=y+my[i];
if(xx<||xx>n||yy<||yy>m||!map[xx][yy]||pre_dis[xx][yy]!=-)continue;
pre_dis[xx][yy]=pre_dis[x][y]+;
nxt.x=xx;nxt.y=yy;
q.push(nxt);
}
}
if(d==)return;
int id=get_id(sx,sy);
for(int i=;i<;i++)
if(pre_dis[sx+mx[i]][sy+my[i]]>)
add(id+d,id+i,pre_dis[sx+mx[i]][sy+my[i]]);
add(id+d,get_id(ex,ey)+(d+)%,); //这是让空格和指定格交换0123标号的妙处
}
spfa最短路 起点可能不在图中,再跑bfs连边。
最后的目标状态就是指定棋子在tx,ty,然后枚举空格子在tx,ty的上下左右求最小。
代码:
瞎bfs
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std; int ans,n,m,qx,tx,ty;
int t[][],vis[][][][];
int mx[]={,,,-},
my[]={-,,,};
struct Node{
int ex,ey,sx,sy,ste;
}now;
queue<Node>q; void read(int &x){
char ch=getchar();x=;int f=;
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-;
for(;isdigit(ch);ch=getchar())x=x*+ch-'';
x=x*f;
} void BFS(){
memset(vis,,sizeof(vis));
Node tmp,cur;
vis[now.sx][now.sy][now.ex][now.ey]=true;
while(!q.empty())q.pop();q.push(now);
while(!q.empty()){
cur=q.front();q.pop();
if(cur.sx==tx&&cur.sy==ty){
ans=cur.ste;
return;
}
for(int i=;i<;i++){
tmp=cur;
int xx=tmp.ex+mx[i],yy=tmp.ey+my[i];
if(!t[xx][yy]||xx<||xx>n||yy<||yy>m)continue;
if(xx==tmp.sx&&yy==tmp.sy)tmp.sx=tmp.ex,tmp.sy=tmp.ey;
tmp.ex=xx;tmp.ey=yy;tmp.ste=cur.ste+;
if(!vis[tmp.sx][tmp.sy][tmp.ex][tmp.ey]){
vis[tmp.sx][tmp.sy][tmp.ex][tmp.ey]=true;
q.push(tmp);
}
}
}
} int main(){
read(n);read(m);read(qx);
for(int i=;i<=n;i++) for(int j=;j<=m;j++) read(t[i][j]);
for(;qx;qx--){
read(now.ex);read(now.ey);read(now.sx);read(now.sy);read(tx);read(ty);
now.ste=;ans=n*m;
BFS();
if(ans!=n*m)printf("%d\n",ans);
else printf("-1\n");
}
return ;
}
70
正解
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define maxn 5000
#define inf 2147483647 int n,m,qx,sumedge,ans,ex,ey,sx,sy,tx,ty,p;
int map[][],pre_dis[][],head[maxn],vis[maxn],dis[maxn];
int mx[]={-,,,},
my[]={,,,-};
struct Node{
int x,y;
}cur,nxt;
queue<Node>q;
queue<int>qn; struct Edge{
int x,y,z,nxt;
Edge(int x=,int y=,int z=,int nxt=):
x(x),y(y),z(z),nxt(nxt){}
}edge[maxn]; void add(int x,int y,int z){
edge[++sumedge]=Edge(x,y,z,head[x]);
head[x]=sumedge;
} int get_id(int i,int j){
return (i-)*m*+(j-)*;
} void bfs(int ex,int ey,int sx,int sy,int d){
memset(pre_dis,-,sizeof(pre_dis));
pre_dis[ex][ey]=;pre_dis[sx][sy]=;
Node now,nxt;now.x=ex;now.y=ey;
while(!q.empty())q.pop();
q.push(now);
while(!q.empty()){
now=q.front();q.pop();
int x=now.x,y=now.y;
for(int i=;i<;i++){
int xx=x+mx[i],yy=y+my[i];
if(xx<||xx>n||yy<||yy>m||!map[xx][yy]||pre_dis[xx][yy]!=-)continue;
pre_dis[xx][yy]=pre_dis[x][y]+;
nxt.x=xx;nxt.y=yy;
q.push(nxt);
}
}
if(d==)return;
int id=get_id(sx,sy);
for(int i=;i<;i++)
if(pre_dis[sx+mx[i]][sy+my[i]]>)
add(id+d,id+i,pre_dis[sx+mx[i]][sy+my[i]]);
add(id+d,get_id(ex,ey)+(d+)%,);
} void spfa(int sx,int sy){
memset(dis,-,sizeof(dis));
memset(vis,,sizeof(vis));
while(!qn.empty())qn.pop();
int id=get_id(sx,sy);
for(int i=;i<;i++)
if(pre_dis[sx+mx[i]][sy+my[i]]!=-){
dis[id+i]=pre_dis[sx+mx[i]][sy+my[i]];
qn.push(id+i);
}
while(!qn.empty()){
int cur=qn.front();qn.pop();vis[cur]=false;
for(int i=head[cur];i;i=edge[i].nxt){
int v=edge[i].y;
if(dis[v]>dis[cur]+edge[i].z||dis[v]==-){
dis[v]=dis[cur]+edge[i].z;
if(!vis[v]){
vis[v]=true;
qn.push(v);
}
}
}
}
} int main(){
scanf("%d%d%d",&n,&m,&qx);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
scanf("%d",&map[i][j]);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(map[i][j]){
if(map[i-][j])bfs(i-,j,i,j,);
if(map[i][j+])bfs(i,j+,i,j,);
if(map[i+][j])bfs(i+,j,i,j,);
if(map[i][j-])bfs(i,j-,i,j,);
}
for(int i=;i<=qx;i++){
scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
if(sx==tx&&sy==ty){
printf("0\n");
continue;
}
bfs(ex,ey,sx,sy,);
ans=inf;
spfa(sx,sy);
int id=get_id(tx,ty);
for(int j=;j<;j++)
if(dis[id+j]!=-)ans=min(ans,dis[id+j]);
if(ans==inf) ans=-;
printf("%d\n",ans);
}
return ;
}
AC
洛谷P1979 华容道的更多相关文章
- 洛谷 P1979 华容道 解题报告
P1979 华容道 题目描述 小\(B\)最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时 ...
- 洛谷P1979 华容道(70分 暴力)
P1979 华容道 题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少 ...
- [NOIP2013] 提高组 洛谷P1979 华容道
题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间. 小 ...
- 洛谷P1979华容道
题目 此题目中存在三种棋盘的放置方法(空白,不能活动,能活动). 而每次变化的格子一定在当前空白格子的周围,因此只需要对空白格子的周围四个状态考虑即可,因此我们设\(a[i][j][k]\)为白格子在 ...
- 洛谷P1979 [NOIP2013提高组Day2T3]华容道
P1979 华容道 题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少 ...
- 洛谷 P1979 [ NOIP 2013 ] 华容道 —— bfs + 最短路
题目:https://www.luogu.org/problemnew/show/P1979 真是一道好题... 首先考虑暴力做法,应该是设 f[i][j][x][y] 记录指定棋子和空格的位置,然后 ...
- 洛谷 1979 华容道——最短路+dp
题目:https://www.luogu.org/problemnew/show/P1979 感到无从下手.但不妨用dp的角度来看.因为空格只有在指定棋子的旁边才有用,所以状态记成制定棋子的位置与空格 ...
- [NOIP2013 提高组] 华容道 P1979 洛谷
[NOIP2013 提高组] 华容道 P1979 洛谷 强烈推荐,更好的阅读体验 经典题目:spfa+bfs+转化 题目大意: 给出一个01网格图,和点坐标x,y空格坐标a,b,目标位置tx,ty要求 ...
- 洛谷1640 bzoj1854游戏 匈牙利就是又短又快
bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...
随机推荐
- wyx20162314实验报告1
北京电子科技学院BESTI实验报告 课程:程序设计与数据结构 班级: 1623 姓名: 王译潇 学号:20162310 指导教师:娄佳鹏老师.王志强老师 实验日期:2017年3月26号 实验密级: 非 ...
- 近千节点的Redis Cluster高可用集群案例:优酷蓝鲸优化实战(摘自高可用架构)
(原创)2016-07-26 吴建超 高可用架构导读:Redis Cluster 作者建议的最大集群规模 1,000 节点,目前优酷在蓝鲸项目中管理了超过 700 台节点,积累了 Redis Clus ...
- mysql查询结果乱码
1.查询数据库结果乱码 mysql,; +-----------+-------------------------------------------------------------+ | do ...
- Spring Boot 中全局异常处理器
Spring Boot 中全局异常处理器,就是把错误异常统一处理的方法.等价于Springmvc中的异常处理器. 步骤一:基于前面的springBoot入门小demo修改 步骤二:修改HelloCon ...
- Oauth2.0认证原理
Oauth2.0 认证协议 Oauth2.0 应用场景: 微信联合登录 授权管理 互联网开放平台互相调用保证安全 微信提供api 给toov5调用 然后就可以获取一些微信的信息 比如微信 ...
- Effective C++ 条款05:了解C++编写并调用哪些函数
规则一 编译器默认操作 // 你认为 class Empty { }; // 实际上 class Empty { public: Empty() { ... } // default 构造函数 Emp ...
- windchill相关功能操作
1.创建产品 2.创建文件夹 3.创建文档 4.创建用户账号 5.创建组 6.创建更改请求 7.创建部件新视图版本 8.创建可重用属性和全局枚举 9.在组织内分配上下文 ...
- hdu 5920 Wool 思路
Wool Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others) Problem D ...
- Pandas分类数据
通常实时的数据包括重复的文本列.例如:性别,国家和代码等特征总是重复的.这些是分类数据的例子. 分类变量只能采用有限的数量,而且通常是固定的数量.除了固定长度,分类数据可能有顺序,但不能执行数字操作. ...
- 如何用VC编写供PB调用的DLL
和编写一般的DLL方法相同,需要注意以下两点: (1)调用约定 c函数有_stdcall._cdecl._fastcall等多种调用约定,调用约定用来说明函数参数的压栈顺序和由谁(函数自身还是调用者) ...