华容道题解 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 ...
随机推荐
- linux多线程同步的四种方式
1. 在并发情况下,指令执行的先后顺序由内核决定.同一个线程内部,指令按照先后顺序执行,但不同线程之间的指令很难说清楚是哪一个先执行.如果运行的结果依赖于多线程执行的顺序,那么就会形成竞争条件,每次运 ...
- 谷歌OKR指导手册 (译)
这是一本关于 OKR 迷你小册子,名为<google OKR playbook>,由 www.whatMatters.com 网站发布. 该网站由John Doerr 团队经营, 而Joh ...
- JAVA知识总结(三):继承和访问修饰符
今天乘着还有一些时间,把上次拖欠的面向对象编程三大特性中遗留的继承和多态给简单说明一下.这一部分还是非常重要的,需要仔细思考. 继承 继承:它是一种类与类之间的关系,通过使用已存在的类作为基础来建立新 ...
- 最简单的手机预览WEB移动端网页的方法
网上看了很多关于该问题的解决办法,各种各样的都有,个人也测试了一些, 最后总结出一个最简单且实用的方法. 1.安装nodejs node官网下载对应版本的nodejs,安装好了之后,在node.js执 ...
- [hdu5316]线段树
题意:给一个array,有两种操作,(1)修改某一个位置的值,(2)询问区间[L,R]内的最大子段和,其中子段需满足相邻两个数的位置的奇偶性不同 思路:假设对于询问操作没有奇偶性的限制,那么记录区间的 ...
- STM32编程:是时候深入理解栈了
[导读] 从这篇文章开始,将会不定期更新关于嵌入式C语言编程相关的个人认为比较重要的知识点,或者踩过的坑. 为什么要深入理解栈?做C语言开发如果栈设置不合理或者使用不对,栈就会溢出,溢出就会遇到无法预 ...
- 前端面试题-WebSocket的实现和应用
(1)什么是WebSocket? WebSocket是HTML5中的协议,支持持久连续,http协议不支持持久性连接.Http1.0和HTTP1.1都不支持持久性的链接,HTTP1.1中的keep-a ...
- Docker之镜像地址
转载自https://www.cnblogs.com/doraman/p/9570891.html 官方docker hub 官方:https://hub.docker.com/explore/ 常用 ...
- Linux系统如何安装qt-desinger
前言:最近想在python3.7.3下玩下PyQt5,写UI有两种方式一种是使用手写,第二个就是使用Qt Designer工具来写,所以首先就是安装PyQt5和pyqt5-tools工具了. 一.安装 ...
- Sentinel源码解析三(滑动窗口流量统计)
前言 Sentinel的核心功能之一是流量统计,例如我们常用的指标QPS,当前线程数等.上一篇文章中我们已经大致提到了提供数据统计功能的Slot(StatisticSlot),StatisticSlo ...