华容道题解 NOIP2013 思路题!
第一次发紫题题解,居然在发布前太激动,把刚写好的还没发布的题解一个Ctrl+A和Backspace全删了。(所以这是二稿)
前置:
做本题一定要有的一些思想:
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 思路题!的更多相关文章
- 51nod P1305 Pairwise Sum and Divide ——思路题
久しぶり! 发现的一道有意思的题,想了半天都没有找到规律,结果竟然是思路题..(在大佬题解的帮助下) 原题戳>>https://www.51nod.com/onlineJudge/ques ...
- Leetcode 简略题解 - 共567题
Leetcode 简略题解 - 共567题 写在开头:我作为一个老实人,一向非常反感骗赞.收智商税两种行为.前几天看到不止两三位用户说自己辛苦写了干货,结果收藏数是点赞数的三倍有余,感觉自己的 ...
- POJ 1904 思路题
思路: 思路题 题目诡异地给了一组可行匹配 肯定有用啊-. 就把那组可行的解 女向男连一条有向边 如果男喜欢女 男向女连一条有向边 跑一边Tarjan就行了 (这个时候 环里的都能选 "增广 ...
- BZOJ 3252: 攻略(思路题)
传送门 解题思路 比较好想的一道思路题,结果有个地方没开\(long\) \(long\) \(wa\)了三次..其实就是模仿一下树链剖分,重新定义重儿子,一个点的重儿子为所有儿子中到叶节点权值最大的 ...
- BZOJ 1303: [CQOI2009]中位数图(思路题)
传送门 解题思路 比较好想的思路题.首先肯定要把原序列转化一下,大于\(k\)的变成\(1\),小于\(k\)的变成\(-1\),然后求一个前缀和,还要用\(cnt[]\)记录一下前缀和每个数出现了几 ...
- [NOIP2013]华容道 题解
[NOIP2013]华容道 首先是一种比较显然的做法. 整个棋盘,除了起点,终点和空格,其他的方块是等价的. 对于终点,它始终不会变化,如果搜到终点结束搜索即可,所以我们不需要考虑终点. 所以需要考虑 ...
- [NOIP2013]华容道 题解(搜索)
[NOIP2013]华容道 [题目描述] 这道题根据小时候玩华容道不靠谱的经验还以为是并查集,果断扑街.考后想想也是,数据这么小一定有他的道理. 首先由于是最小步数,所以BFS没跑了.那么我们大可把这 ...
- ACM 杂题,思路题 整理
UVa 11572 - Unique Snowflakes 问一个数组中,无重复数字的最长子串长度是多少. 用map维护某数字上次出现的位置.另外用变量last表示上次出现数字重复的位置. 如果出现重 ...
- 【思路题】【多校第一场】【1001.OO’s Sequence】
题目大意: 给你一个序列A,f(l,r) 表示 在[l,r]中 的Ai 对于每一个数Aj 都有 Ai%Aj!=0 的数目( i!=j ) 卡了一段时间..... 题解 简单题 定义两个数组L[i ...
随机推荐
- App 抓包代理设置
1.设置 Fiddler 打开 Fiddler,Tools --> Fiddler Options --> HTPS (配置完后记得要重启 Fiddler) 选中 "D ...
- postman(介绍)
Postman 界面介绍 一. 安装后首次打开 postman,会提示你是否需要登录,登录的话可以云端保存你的收藏及历史记录,不登陆不影响使用. 二. 进入后就是如下图所示的界面了.看到这么多按钮 ...
- muduo网络库源码学习————线程池实现
muduo库里面的线程池是固定线程池,即创建的线程池里面的线程个数是一定的,不是动态的.线程池里面一般要包含线程队列还有任务队列,外部程序将任务存放到线程池的任务队列中,线程池中的线程队列执行任务,也 ...
- 认识mysql3个基本库
一.3个基本库 数据库初始化安装完毕会有三个基本库mysql .information_schema.performace_schema.作为应用程序开发者,平时较少关注这些数据库尤其是后两者.但是通 ...
- 记录一下关于在工具类中更新UI使用RunOnUiThread犯的极其愚蠢的错误
由于Android中不能在子线程中更新ui,所以平时在子线程中需要更新ui时可以使用Android提供的RunOnUiThread接口,但是最近在写联网工具类的时候,有时候会出现联网异常,这个时候为了 ...
- 【Scala】新手入门,基础语法概览
目录 变量.常量和数据类型 var val 数据类型 条件表达式 块表达式 to循环 for循环 for推导式 scala中的方法和函数 方法的定义 函数的定义 函数和方法的区别 变量.常量和数据类型 ...
- PHP导出excel文件,第二步先实现自写二维数组加入模板excel文件后导出
今天主要研究数据加入EXCEL并导出的问题,先不从数据库提取数据导出,自己先写一个二维数组,然后遍历二维数组写入excel模板中导出,首先根据模板excel的内容书写对应的二维数组 $arr=arra ...
- Linux内核驱动学习(六)GPIO之概览
文章目录 前言 功能 如何使用 设备树 API 总结 前言 GPIO(General Purpose Input/Output)通用输入/输出接口,是十分灵活软件可编程的接口,功能强大,十分常用,SO ...
- SpringBoot 整合SpringBatch实际项目改造
SpringBoot整合SpringBatch项目,已将代码开源至github,访问地址:https://github.com/cmlbeliever/SpringBatch 欢迎star or fo ...
- 前端面试题-几个很实用的BOM属性对象方法?
什么是Bom? Bom是浏览器对象.有哪些常用的Bom属性呢? (1)location对象 location.href-- 返回或设置当前文档的URL location.search -- 返回URL ...