神の契约

题目大意:自己看去...

题解:做了一下午...本蒟蒻立志要写全网最详细的题解。╭(╯^╰)╮

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 华容道的更多相关文章

  1. 洛谷 P1979 华容道 解题报告

    P1979 华容道 题目描述 小\(B\)最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时 ...

  2. 洛谷P1979 华容道(70分 暴力)

    P1979 华容道 题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少 ...

  3. [NOIP2013] 提高组 洛谷P1979 华容道

    题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间. 小 ...

  4. 洛谷P1979华容道

    题目 此题目中存在三种棋盘的放置方法(空白,不能活动,能活动). 而每次变化的格子一定在当前空白格子的周围,因此只需要对空白格子的周围四个状态考虑即可,因此我们设\(a[i][j][k]\)为白格子在 ...

  5. 洛谷P1979 [NOIP2013提高组Day2T3]华容道

    P1979 华容道 题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少 ...

  6. 洛谷 P1979 [ NOIP 2013 ] 华容道 —— bfs + 最短路

    题目:https://www.luogu.org/problemnew/show/P1979 真是一道好题... 首先考虑暴力做法,应该是设 f[i][j][x][y] 记录指定棋子和空格的位置,然后 ...

  7. 洛谷 1979 华容道——最短路+dp

    题目:https://www.luogu.org/problemnew/show/P1979 感到无从下手.但不妨用dp的角度来看.因为空格只有在指定棋子的旁边才有用,所以状态记成制定棋子的位置与空格 ...

  8. [NOIP2013 提高组] 华容道 P1979 洛谷

    [NOIP2013 提高组] 华容道 P1979 洛谷 强烈推荐,更好的阅读体验 经典题目:spfa+bfs+转化 题目大意: 给出一个01网格图,和点坐标x,y空格坐标a,b,目标位置tx,ty要求 ...

  9. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

随机推荐

  1. 实验四 Android程序设计 实验报告 20162305李昱兴

    实验四 Android程序设计 实验报告 20162305李昱兴 一.Android Studio的安装测试 1.有关该软件 Android Studio,是基于Itellij IDEA的一款流行的I ...

  2. Arcgis Javascript API 开发笔记

    JS API3.4的要求 à(1)  IE9或以上版本 否则dijit1.8.3不匹配 1.如何发布ArcgisJavascript API应用 0.准备工作: (1).有web应用: (2).有js ...

  3. jQuery的序列化元素 serialize()方法 serializeArray()方法 param()方法

    当提交的表单元素较多时用serialize()方法,serialize()方法也是作用于一个jQuery的对象,它能够将DOM元素内容序列化为字符串,用于Ajax请求. serialize() 方法通 ...

  4. Elasticsearch 配置优化

    cluster.routing.allocation.same_shard.host:true 这会防止同一个shard的主副本存在同一个物理机上(因为如果存在一个机器上,副本的高可用性就没有了). ...

  5. JavaScript tips —— target与currentTarget的区别

    定义 以下是红宝书的描述 属性/方法 类型 读/写 说明 currentTarget Element 只读 其事件处理程序当前正在处理事件的那个元素 target Element 只读 事件的目标 M ...

  6. ButterKnife使用详谈

    (1)ButterKnife是什么? 在开发过程中,我们总是会写大量的findViewById和点击事件,像初始view.设置view监听这样简单而重复的操作让人觉得特别麻烦,当然不会偷懒的程序员不是 ...

  7. WPF关于改变ListBoxItem的颜色的注意事项以及如何找到ListBox中的ItemsPanel

    在ListBox中碰到过几个问题,现在把它写出来: 第一个就是在ListBoxItem中当我用触发器IsSelected和IsMouseOver来设置Background和Foreground的时候, ...

  8. BusyIndicator using MVVM 忙碌状态指示器的的实现

    ViewModel 视图模型 public abstract class ViewModelBase : INotifyPropertyChanged { private bool isbusy; p ...

  9. Autolayout .Compact or .Regular [iPhone/iPad]

  10. jquery attr与prop的区别与联系

    最近开发中发现用attr无法设置checkbox的选中事件,在网上找了下说要用prop,所以总结下两者的区别. 1.操作的对象不同 attr:操作的是HTML文档节点属性 prop:操作的是js对象属 ...