[HAOI2008]移动玩具

题目描述

在一个

4

×

4

4\times4

4×4 的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动时只能将玩具向上下左右四个方向移动,并且移动的位置不能有玩具,请你用最少的移动次数将初始的玩具状态移动到某人心中的目标状态。

输入格式

4

4

4 行表示玩具的初始状态,每行

4

4

4 个数字

1

\texttt{1}

1 或

0

\texttt{0}

0,

1

\texttt{1}

1 表示方格中放置了玩具,

0

\texttt{0}

0 表示没有放置玩具。接着是一个空行。接下来

4

4

4 行表示玩具的目标状态,每行

4

4

4 个数字

1

\texttt{1}

1 或

0

\texttt{0}

0,意义同上。

输出格式

一个整数,所需要的最少移动次数。

输入输出样例

样例输入1

1111
0000
1110
0010 1010
0101
1010
0101

样例输出1

4

讲解

变量

pre数组表示初始状态,dec1表示初始状态转化的十进制数。
tar数组表示目标状态,dec2表示目标状态转换的十进制数。
vis数组表示是否被访问过。
q队列用来存储玩具摆放的状态。
dx和dy数组是为了方便表示扩展而使用的。
有关玩具放置的状态的转化请移步至函数1。

函数1

int BintoDec(int a[6][6])
此函数的目的:
根据a数组,将玩具摆放的状态转化为十进制数。
变量解释:
a数组表示要转换的玩具摆放的状态。
具体思想:
由于每种玩具摆放的状态都是唯一的,
所以我们尝试着将其转化为一个数字。
大家可以发现,每种玩具摆放的状态其实是二进制的表示。
所以我们可以将其转化为十进制的数。
例如:样例中,初始状态可以转化为:1111000011100010。
所以可以将其转化为十进制的数。
可能你会考虑爆long long或爆空间的问题。
放心,玩具摆放的状态用十进制表示必不会超过2^16-1。
所以,我们可以将样例中的起始状态和目标状态
分别表示为61666和42405。
具体怎么转化呢?
我们先画一个玩具摆放的状态图:

列1 列2 列3 列4
15 14 13 12
11 10 9 8
7 6 5 4
3 2 1 0

可以发现,每个格子对应的乘方有如下的规律:
设格子(i,j)对应的乘方为G(i,j),
则有:G(i,j) = 16 - (i - 1) * 4 - j。
这个稍微想一下就可以证明出来,这里不再赘述。

函数2

void DectoBin(int x, int a[6][6])
此函数的目的:
根据x,将十进制状态转换回一开始二进制的状态。
变量解释:
x表示要转换回的十进制数,
a数组表示转换后的二进制状态。
具体思想:
呃。。。这就不用解释了吧。
这个大家应该很明白吧。
不明白就去看有关二进制的内容吧。

函数3

int judge(int x0, int y0, int xx, int yy)
此函数的目的:
判断是否越过边界且点所表示的数是否相同,是返回1,否表示0
变量解释:
xx,yy分别是点(x0,y0)经过扩展后得到的点的横坐标和纵坐标。
具体思想:\

函数4

void bfs()
此函数的目的:
通过bfs求得最少步数。
变量解释:
具体思想:
基本的bfs(STL<queue>)操作:
1. 将初始状态入队,并标记在队列中(vis[x]=1
2. 在队列不为空时循环执行以下操作:
(1) 取出队首,将其转化为原来的二进制状态,然后出队。
(2) 向上下左右扩展。
(3) 判断是否越过边界且两数相等。
if No then执行以下操作(最后需要还原a数组状态!):
1_ 提取扩展节点的状态,并将其转化为十进制数。
2_ 将扩展节点与原数交换
3_ 记录交换后的数组对应的十进制数
4_ 判断是否访问过
5_ 若没访问过,则标记已访问过,并记录交换后的数组对应的十进制数的父节点,即交换前的数组对应的十进制数。
6_ 否则直接跳过。
7_ 如果已经达到了目标状态,直接返回。
else then直接跳过

Step 1

输入&转化为二进制
由于输入形式中,数字都是连在一起的。
所以,大多数人都会想到用字符串或字符数组输入每一行,
然后将其转化为数字。但其实可以不用这么麻烦。
大家都知道,占x位输入是这么写的:
scanf("%xd", &a);(假定x是具体数字)
所以,对于一行连在一起的数字,我们可以占一位输入。
就是上面所写的那样。这样就可以使代码简洁明了。
然后再调用上面的BintoDec函数得到初始和目标状态的十进制数。

Step 2

bfs一波走着!
根据上面的函数,保证会有解。

Step 3

递进求答案、输出&程序结束

Code

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std; int pre[6][6], tar[6][6], vis[65539], f[65539], dec1, dec2;
queue<int> q;
const int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
/*
变量:
pre数组表示初始状态,dec1表示初始状态转化的十进制数。
tar数组表示目标状态,dec2表示目标状态转换的十进制数。
vis数组表示是否被访问过。
q队列用来存储玩具摆放的状态。
dx和dy数组是为了方便表示扩展而使用的。
有关玩具放置的状态的转化请移步至函数1。
*/
int BintoDec(int a[6][6]) {
/*
函数1:int BintoDec(int a[6][6])
此函数的目的:
根据a数组,将玩具摆放的状态转化为十进制数。
变量解释:
a数组表示要转换的玩具摆放的状态。
具体思想:
由于每种玩具摆放的状态都是唯一的,
所以我们尝试着将其转化为一个数字。
大家可以发现,每种玩具摆放的状态其实是二进制的表示。
所以我们可以将其转化为十进制的数。
例如:样例中,初始状态可以转化为:1111000011100010。
所以可以将其转化为十进制的数。
可能你会考虑爆long long或爆空间的问题。
放心,玩具摆放的状态用十进制表示必不会超过2^16-1。
所以,我们可以将样例中的起始状态和目标状态
分别表示为61666和42405。
具体怎么转化呢?
我们先画一个玩具摆放的状态图:
1 2 3 4
1 15 14 13 12
2 11 10 9 8
3 7 6 5 4
4 3 2 1 0
可以发现,每个格子对应的乘方有如下的规律:
设格子(i,j)对应的乘方为G(i,j),
则有:G(i,j) = 16 - (i - 1) * 4 - j。
这个稍微想一下就可以证明出来,这里不再赘述。
*/
int res = 0;
for(int i = 4; i >= 1; --i)
for(int j = 4; j >= 1; --j)
res += a[i][j] * pow(2, 16 - (i - 1) * 4 - j);
return res;
} void DectoBin(int x, int a[6][6]) {
/*
函数2:void DectoBin(int x, int a[6][6])
此函数的目的:
根据x,将十进制状态转换回一开始二进制的状态。
变量解释:
x表示要转换回的十进制数,
a数组表示转换后的二进制状态。
具体思想:
呃。。。这就不用解释了吧。
这个大家应该很明白吧。
不明白就去看有关二进制的内容吧。
*/
while(x) {
for(int i = 4; i >= 1; --i)
for(int j = 4; j >= 1; --j) {
a[i][j] = x % 2;
x /= 2;
}
}
//转换完毕
}
int judge(int x0, int y0, int xx, int yy) {
/*
函数3:int judge(int x0, int y0, int xx, int yy)
此函数的目的:
判断是否越过边界且点所表示的数是否相同,是返回1,否表示0
变量解释:
xx,yy分别是点(x0,y0)经过扩展后得到的点的横坐标和纵坐标。
具体思想:\
*/
return (xx >= 1) && (xx <= 4) && (yy >= 1) && (yy <= 4) && (pre[x0][y0] != pre[xx][yy]);
}
void bfs() {
/*
函数4:void bfs()
此函数的目的:
通过bfs求得最少步数。
变量解释:\
具体思想:
基本的bfs(STL<queue>)操作:
1. 将初始状态入队,并标记在队列中(vis[x]=1)
2. 在队列不为空时循环执行以下操作:
(1)取出队首,将其转化为原来的二进制状态,然后出队。
(2)向上下左右扩展。
(3)判断是否越过边界且两数相等。
if No then执行以下操作(最后需要还原a数组状态!):
1_ 提取扩展节点的状态,并将其转化为十进制数。
2_ 将扩展节点与原数交换
3_ 记录交换后的数组对应的十进制数
4_ 判断是否访问过
5_ 若没访问过,则标记已访问过,并记录交换后的数组对应的十进制数的父节点,即交换前的数组对应的十进制数。
6_ 否则直接跳过。
7_ 如果已经达到了目标状态,直接返回。
else then直接跳过
*/
q.push(dec1);
vis[dec1] = 1;/*注意!一定要加!!!*/
while(q.size()/*或者可以写!q.empty()*/) {
int now = q.front();
DectoBin(now, pre);
q.pop();
for(int i = 1; i <= 4; ++i)
for(int j = 1; j <= 4; ++j) {
int x0 = i, y0 = j;
for(int k = 0; k < 4; ++k) {
int xx = x0 + dx[k], yy = y0 + dy[k], flag = 0;
if(judge(x0, y0, xx, yy)) {
flag = 1;
int tmpdec1 = BintoDec(pre);
swap(pre[x0][y0], pre[xx][yy]);
int tmpdec2 = BintoDec(pre);
if(!vis[tmpdec2]) {
vis[tmpdec2] = 1;
f[tmpdec2] = tmpdec1;
q.push(tmpdec2);
}
if(tmpdec2 == dec2) return;
}
if(flag)
swap(pre[x0][y0], pre[xx][yy]);
}
}
}
}
int main() {
/*
Step 1:输入&转化为二进制
由于输入形式中,数字都是连在一起的。
所以,大多数人都会想到用字符串或字符数组输入每一行,
然后将其转化为数字。但其实可以不用这么麻烦。
大家都知道,占x位输入是这么写的:
scanf("%xd", &a);(假定x是具体数字)
所以,对于一行连在一起的数字,我们可以占一位输入。
就是上面所写的那样。这样就可以使代码简洁明了。
然后再调用上面的BintoDec函数得到初始和目标状态的十进制数。
*/
for(int i = 1; i <= 4; ++i)
for(int j = 1; j <= 4; ++j)
scanf("%1d", &pre[i][j]);
for(int i = 1; i <= 4; ++i)
for(int j = 1; j <= 4; ++j)
scanf("%1d", &tar[i][j]);
dec1 = BintoDec(pre), dec2 = BintoDec(tar);
/*
Step 2:bfs一波走着!
根据上面的函数,保证会有解。
*/
bfs();
f[dec1] = 0;
int ans = 0;
/*
Step 3:递进求答案、输出&程序结束
*/
while(f[dec2]) ans++, dec2 = f[dec2];
printf("%d", ans);
return 0;
}

P4289 【一本通提高篇广搜的优化技巧】[HAOI2008]移动玩具的更多相关文章

  1. 总结-一本通提高篇&算竞进阶记录

    当一个人看见星空,就再无法忍受黑暗 为了点亮渐渐沉寂的星空 不想就这样退役 一定不会鸽の坑 . 一本通提高篇 . 算竞进阶 . CDQ & 整体二分 . 平衡树 . LCT . 字符串 . 随 ...

  2. 一本通提高篇——斜率优化DP

    斜率优化DP:DP的一种优化形式,主要用于优化如下形式的DP f[i]=f[j]+x[i]*x[j]+... 学习可以参考下面的博客: https://www.cnblogs.com/Xing-Lin ...

  3. 信息学奥赛一本通 提高篇 序列第k个数 及 快速幂

    我是传送门 这个题首先是先判断是等差还是等比数列 等差的话非常简单: 前后两个数是等差的,举个栗子: 3 6 9 12 这几个数,(我感觉 1 2 3 4并说明不了什么) 每次都加3嘛,很容易看出,第 ...

  4. P2512 【一本通提高篇贪心】「一本通 1.1 练习 6」[HAOI2008]糖果传递

    [HAOI2008]糖果传递 题目描述 有 n n n 个小朋友坐成一圈,每人有 a i a_i ai​ 个糖果.每人只能给左右两人传递糖果.每人每次传递一个糖果代价为 1 1 1. 输入格式 小朋友 ...

  5. 提高Web性能的前端优化技巧总结

  6. 提高篇(1):RMQ问题与ST表

    RMQ是英文Range Minimum/Maximum Query的缩写,是询问某个区间内的最值,这里讲一种解法:ST算法 ST算法通常用在要多次(10^6级别)询问区间最值的问题中,相比于线段树,它 ...

  7. 【双向广搜+逆序数优化】【HDU1043】【八数码】

    HDU上的八数码 数据强的一B 首先:双向广搜 先处理正向搜索,再处理反向搜索,直至中途相遇 visit 和 队列都是独立的. 可以用一个过程来完成这2个操作,减少代码量.(一般还要个深度数组) 优化 ...

  8. Secret Milking Machine POJ - 2455 网络流(Dinic算法---广搜判断+深搜增广)+时间优化+二分

    题意: 第一行输入N M C ,表示从1到N有M条无向边,现在要从1走到N 走C次完全不同的路径,求最长边的最小值.下面M行是从a点到b点的距离. 建图: 题上说从两点之间可以有多条边,问的是从1~N ...

  9. Java提高篇(三三)-----Map总结

    在前面LZ详细介绍了HashMap.HashTable.TreeMap的实现方法,从数据结构.实现原理.源码分析三个方面进行阐述,对这个三个类应该有了比较清晰的了解,下面LZ就Map做一个简单的总结. ...

随机推荐

  1. 『现学现忘』Git基础 — 21、git diff命令

    目录 1.git diff 命令说明 2.比较工作区与暂存区中文件的差别 3.比较暂存区与本地库中文件的差别 4.总结git diff命令常见用法 5.总结 1.git diff 命令说明 在comm ...

  2. 电机噪声之谐波分析(内附simulink中FFT分析的相关参数配置与解析)

    电机噪声之谐波分析(内附simulink中FFT分析的相关参数配置与解析) 目录 电机噪声之谐波分析(内附simulink中FFT分析的相关参数配置与解析) 写在前面 正文 电机噪声 谐波的产生 什么 ...

  3. MinIO学习

    1.Minio及背景 Minio是一个开源的分布式文件存储系统,它基于 Golang 编写,虽然轻量,却拥有着不错的高性能,可以将图片.视频.音乐.pdf这些文件存储到多个主机,可以存储到多个Linu ...

  4. Probabilistic two-stage detection

  5. 16岁男生信息竞赛成瘾心理出现问题 妈妈:他竟说要AK我

    16岁男生信息竞赛成瘾心理出现问题 -- 妈妈:他竟说要AK我 "我儿子最近快走火入魔了,医生,你救救他吧."40出头的林女士拉着儿子走进江苏省人民医院临床心理科.近几年,信息竞赛 ...

  6. fiddler的安装以及使用同时对Android 与IOS 抓包配置进行分析 进阶 一

    由于工作方向的原因,很久没有用过APP抓包工具了,有那么一天遇到了bug需要协助开发工程师进行定位分析,然后又重新梳理了一下之前常用的抓包工具,这里重点介绍一下目前市面上最流行的几款抓包工具,根据自己 ...

  7. React项目配置npm run build命令分环境打包

    使用create-react-app脚手架创建的项目默认隐藏了webpack等配置文件信息,使用npm run eject命令暴露这些隐藏的配置文件信息 项目默认有两个环境:开发环境(npm star ...

  8. 爱快在PVE下不定时反复重启死机的解决方法

    太长不看版本: 爱快3.6.X在PVE乃至于ESXI下都存在一定的兼容问题! 官网下载3.6.X安装后进入系统设置-升级备份-版本升级,使用爱快3.4.9bin升降级包,下载其中的bin升降级包,将爱 ...

  9. 【Java面试】请简单说一下你对受检异常和非受检异常的理解

    Hi,我是Mic 今天给大家分享一道阿里一面的面试题. 这道题目比较基础,但是确难倒了很多人. 关于"受检异常和非受检异常的理解" 我们来看看普通人和高手的回答. 普通人: 嗯.. ...

  10. 记安装AWVS14过程踩的坑

    由于之前的AWVS14用着用着无法扫描了,一扫就是失败,一气之下就重装系统了.重装系统后发现安装还是不行,折腾了好久,终于找到方法了. 安装acunetix_14.1.210324124.exe 没啥 ...