第一次发紫题题解,居然在发布前太激动,把刚写好的还没发布的题解一个Ctrl+A和Backspace全删了。(所以这是二稿)

luogu题目传送门

前置:

做本题一定要有的一些思想:

1、从简思想: 模拟白格子的移动,而千万不要想这去模拟众棋子的移动。这样会简单很多,否则会s的很惨。

2、转换思想(万物皆有其对立面):题目中给的规则,是棋子可以移动到白色格子上。我们将其共轭一下:

白色格子可以移动到棋子上,或者说,白色格子可以和棋子交换位置。

3、白色格子永远只有一个。(看似是废话的大实话)

4、习惯把起点叫做x,终点叫作ht

暴力那些事:

暴力不难实现,就是令空白格子到处移动,然后不断对ans取min就好了。别的题解有很多详细介绍,

在此不过多赘述。

思路获取:

First,从题目基本的信息入手。

如何才能令我们的目标棋子移动?

稍作思考。。。。。。当然是让空白格子在目标旗子旁边啊!!

一个要点get。

Second, 对白色格子全局的移动进行试验:

发现,白色格子的移动可以大致分为两个阶段:不顾一切移向目标棋子,然后在棋子周围打转(因为这样才可以步数最少)

だから(So),我们重点关注的对象,自然就来到了第二阶段上。

不难发现,无论棋子在哪个坐标上,白色格子永远都只会有四种情况:上,下,左,右。

移动后呢?    发现:  从一次移动到下一次移动,白色格子和棋子只会出现两大类移动:

1、白色格子还在棋子周围,只不过是上下左右随机。

2、白色格子直接和棋子位置进行了交换。

再看题目要求:最短路径。

那么,对于第一种情况,我们完全可以用BFS,求出白色格子从上一次位置移动到目前位置的距离。

第二种情况,步数很明显是1.

又一个要点get.

现在考虑,如何对我们的状态进行记录?

考虑用三维数组ok[x][y][k] (坐标x,y + 状态编号k)记录。 (其中k:k = 0,1,2,3,分别对应上下左右即可)

最后,再看一下题目的数据范围:我们完全可以求出(以棋子为中心)所有坐标到相邻坐标的步数。

!!!关系如此密集,何不考虑建图呢?

如果将所有的状态看做一个点,那么他到下一个状态的边权就是他们的步数。如果我们对这个图进行SPFA呢?

在那一串dist中,我们要求的距离,不正是终点ht周围的4个状态对应的dist吗!(取min,废话)

重大要点get!!

At last, 如何对这些状态进行编号,跑SPFA?不能直接用三维数组建图啊!

你需要一个公式:((x - 1) * m + y) * 4 - (4 - k);

至此,这道题也是被我们攻陷了。

后置:

1、一定要注意代码中的细节操作(特别是BFS跑状态的时候)

2、k对应的状态可千万不能忘呀,对应错了后果很严重呀!

码力全开!

#include <bits/stdc++.h>
using namespace std;
#define N 50
#define N2 5005
#define isdigit(c) ((c)>='0'&&(c)<='9')
#define INF (~0u>>1)
const int orz = ; inline int read(){
int x = , s = ;
char c = getchar();
while(!isdigit(c)){
if(c == '-') s = -;
c = getchar();
}
while(isdigit(c)){
x = (x << ) + (x << ) + (c ^ '');
c = getchar();
}
return x * s;
} bool ma[N][N], vis[N][N];
int fx[] = {-, , , };
int fy[] = {, , -, };
int n, m, ex, ey, sx, sy, htx, hty; inline bool judge(int x, int y){
if(x < || y < || x > n || y > m)return ;
return ma[x][y] ;
} inline int getnum(int x, int y, int t){
return ((x - ) * m + y) * - ( - t);
} struct data{
int x ,y;
int step; /*存储每个坐标的信息*/
};
/* sx,sy是否可以到达htx, hty?*/
int bfs(int dx, int dy, int sx, int sy, int htx, int hty){
queue <data> q; /*模拟可移动格子的移动*/
memset(vis, , sizeof(vis));
q.push((data){sx, sy, });
vis[sx][sy] = ; /*从起点开始*/
while(!q.empty()){
data now = q.front();
q.pop();
int x = now.x, y = now.y, step = now.step;
if(x == htx && y == hty){
return now.step; /*如果到达终点*/
}
for(int i = ;i < ; i++){
int l = x + fx[i], r = y + fy[i];
if(judge(l, r)){
if(vis[l][r] || (l == dx && r == dy))continue; /*如果新点已经访问过或者等于现在的*/
q.push((data){l, r, step + });
vis[l][r] = ;
}
}
}
return INF; /*两个点不能互相到达*/
} struct node{
int u, v, w;
int next;
} t[N2];
int f[N2]; int bian = ;
void add(int u, int v, int w){
t[++bian].u = u;
t[bian].v = v;
t[bian].w = w;
t[bian].next = f[u];
f[u] = bian;
return ;
} /*预处理, 找出所有可行状态*/ bool ok[N][N][];
void prepare(){
for(int i = ;i <= n; i++)
for(int j = ;j <= m; j++)
if(ma[i][j])
for(int k = ;k < ; k++)
if(judge(i+fx[k], j+fy[k]))
ok[i][j][k] = ;
for(int i = ;i <= n; i++) /*空白格子围绕棋子旋转时*/
for(int j = ;j <= m; j++)
for(int k = ;k < ; k++)
for(int l = k + ; l < ; l++) /*枚举不同的方向*/
if(ok[i][j][k] && ok[i][j][l]){
int a = getnum(i, j, k), b = getnum(i, j, l);
int c = bfs(i, j, i + fx[k], j + fy[k], i + fx[l], j + fy[l]);
if(c != INF){ /*状态之间是否可达*/
add(a, b, c);
add(b, a, c);
}
}
for(int i = ;i <= n; i++) /*空白格子和棋子交换位置*/
for(int j = ;j <= m; j++){
if(ok[i][j][] && ok[i][j+][]){
int a = getnum(i, j, ); /*几种小情况*/
int b = getnum(i, j+, );
add(a, b, );
add(b, a, );
}
}
for(int i = ;i <= n; i++)
for(int j = ;j <= m; j++){
if(ok[i][j][] && ok[i+][j][]){
int a = getnum(i, j, );
int b = getnum(i+, j, );
add(a, b, );
add(b, a, );
}
}
return ;
} /*进行最短路求解*/ queue <int> q;
bool viss[N2];
int d[N2]; int spfa(){
memset(d, , sizeof(d));
memset(viss, , sizeof(viss));
int flag = d[]; /*之后判断用的*/
for(int i = ;i < ; i++){ /*先对起点进行选取*/
int l = sx + fx[i], r = sy + fy[i];
if(judge(l, r)){
int temp = bfs(sx, sy, ex, ey, l, r); /*ex,ey 是否可以到达 l,r? */
if(temp != INF){
int snum = getnum(sx, sy, i);
viss[snum] = ;
q.push(snum);
d[snum] = temp;
}
}
}
while(!q.empty()){ /*求所有状态间的最短路*/
int now = q.front(); q.pop();
viss[now] = ;
for(int i = f[now]; i;i = t[i].next){
int u = t[i].u, v = t[i].v, w = t[i].w;
if(d[v] > d[u] + w){
d[v] = d[u] + w;
if(!viss[v]){
viss[v] = ;
q.push(v);
}
}
}
}
int ans = INF;
for(int i = ; i < ; i++){
int num = getnum(htx, hty, i);
ans = min(ans, d[num]);
}
return ans == flag ? - : ans;
} int main(){
// freopen("hh.txt", "r", stdin);
n = read(), m = read();
int T = read();
for(int i = ;i <= n; i++)
for(int j = ;j <= m; j++)
ma[i][j] = read();
prepare();
while(T--){ /*e: 空白 s: 起点 ht: 终点*/
ex = read(), ey = read(), sx = read(), sy = read(), htx = read(), hty = read();
if(sx == htx && sy == hty){ /*特判*/
puts("");
continue;
}
if(!ma[htx][hty] || !ma[sx][sy]){ /*如果起点终点根本不在图内*/
puts("-1");
continue;
}
printf("%d\n", spfa());
}
return orz; /*向大佬势力低头 同时拜一下CCf求AC*/
}

(二稿真累)

华容道题解 NOIP2013 思路题!的更多相关文章

  1. 51nod P1305 Pairwise Sum and Divide ——思路题

    久しぶり! 发现的一道有意思的题,想了半天都没有找到规律,结果竟然是思路题..(在大佬题解的帮助下) 原题戳>>https://www.51nod.com/onlineJudge/ques ...

  2. Leetcode 简略题解 - 共567题

    Leetcode 简略题解 - 共567题     写在开头:我作为一个老实人,一向非常反感骗赞.收智商税两种行为.前几天看到不止两三位用户说自己辛苦写了干货,结果收藏数是点赞数的三倍有余,感觉自己的 ...

  3. POJ 1904 思路题

    思路: 思路题 题目诡异地给了一组可行匹配 肯定有用啊-. 就把那组可行的解 女向男连一条有向边 如果男喜欢女 男向女连一条有向边 跑一边Tarjan就行了 (这个时候 环里的都能选 "增广 ...

  4. BZOJ 3252: 攻略(思路题)

    传送门 解题思路 比较好想的一道思路题,结果有个地方没开\(long\) \(long\) \(wa\)了三次..其实就是模仿一下树链剖分,重新定义重儿子,一个点的重儿子为所有儿子中到叶节点权值最大的 ...

  5. BZOJ 1303: [CQOI2009]中位数图(思路题)

    传送门 解题思路 比较好想的思路题.首先肯定要把原序列转化一下,大于\(k\)的变成\(1\),小于\(k\)的变成\(-1\),然后求一个前缀和,还要用\(cnt[]\)记录一下前缀和每个数出现了几 ...

  6. [NOIP2013]华容道 题解

    [NOIP2013]华容道 首先是一种比较显然的做法. 整个棋盘,除了起点,终点和空格,其他的方块是等价的. 对于终点,它始终不会变化,如果搜到终点结束搜索即可,所以我们不需要考虑终点. 所以需要考虑 ...

  7. [NOIP2013]华容道 题解(搜索)

    [NOIP2013]华容道 [题目描述] 这道题根据小时候玩华容道不靠谱的经验还以为是并查集,果断扑街.考后想想也是,数据这么小一定有他的道理. 首先由于是最小步数,所以BFS没跑了.那么我们大可把这 ...

  8. ACM 杂题,思路题 整理

    UVa 11572 - Unique Snowflakes 问一个数组中,无重复数字的最长子串长度是多少. 用map维护某数字上次出现的位置.另外用变量last表示上次出现数字重复的位置. 如果出现重 ...

  9. 【思路题】【多校第一场】【1001.OO’s Sequence】

    题目大意: 给你一个序列A,f(l,r) 表示 在[l,r]中 的Ai 对于每一个数Aj 都有 Ai%Aj!=0  的数目(  i!=j  ) 卡了一段时间..... 题解 简单题 定义两个数组L[i ...

随机推荐

  1. CF--思维练习--CodeForces - 216C - Hiring Staff (思维+模拟)

    ACM思维题训练集合 A new Berland businessman Vitaly is going to open a household appliances' store. All he's ...

  2. 图论--最小生成树--Kruscal 模板

    #include<iostream> #include<queue> #include<algorithm> #include<set> #includ ...

  3. SVN 部署(基于 Linux)

    1.通过 yum 命令安装 svnserve,命令如下: # 此命令会全自动安装svn服务器相关服务和依赖,安装完成会自动停止命令运行 yum -y install subversion # 若需查看 ...

  4. 区间dp C - Two Rabbits

    C - Two Rabbits 这个题目的意思是,n块石头围一圈.一只兔子顺时针,一只兔子逆时针(限制在一圈的范围内). 这个题目我觉得还比较难,不太好想,不过后来lj大佬给了我一点点提示,因为是需要 ...

  5. Angular 从入坑到挖坑 - Router 路由使用入门指北

    一.Overview Angular 入坑记录的笔记第五篇,因为一直在加班的缘故拖了有一个多月,主要是介绍在 Angular 中如何配置路由,完成重定向以及参数传递.至于路由守卫.路由懒加载等&quo ...

  6. 【HBase】通过Bulkload批量加载数据到Hbase表中

    目录 需求 步骤 一.开发MapReduce 二.打成Jar包放到linux执行 三.有两种办法将HFile文件加载到HBase表中 开发代码 Hadoop命令运行 需求 将hdfs上面的这个路径 / ...

  7. 【matlab系列汇总】小白博主的matlab学习实战快速进阶之路(持续更新)

    我把之前在学习和工作中使用matlab的技巧和使用教程等相关整理到这里,方便查阅学习,如果能帮助到您,请帮忙点个赞: MATLAB可以进行矩阵运算.绘制函数和数据.实现算法.创建用户界面.连接其他编程 ...

  8. [hdu5534]DP

    题目原意:给一棵n个点的树添加边,给定度函数f(d)为一个点的度的函数,求所有点的度函数的和 思路: 函数只与点的度有关,而与点无关,n个点的树有n-1条边,共产生2(n-1)个度,每个点至少有1个度 ...

  9. C# 委托delegate的基本用法

    委托:就是一个方法的类型,下面3个调用情况来详细熟悉一下: 1.调用组合委托 //委托:就是一个方法的类型 public delegate int TestDelegateStr(); public ...

  10. 用项目强化你的webpack

    用你的webpack实现vue-cli 本文围绕前端工程化,用webpack从零搭建一个完整项目的过程 本文核心知识点: webpack的使用 vue组件化思想 Element-UI的使用 别走别走, ...