P4289 【一本通提高篇广搜的优化技巧】[HAOI2008]移动玩具
[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]移动玩具的更多相关文章
- 总结-一本通提高篇&算竞进阶记录
当一个人看见星空,就再无法忍受黑暗 为了点亮渐渐沉寂的星空 不想就这样退役 一定不会鸽の坑 . 一本通提高篇 . 算竞进阶 . CDQ & 整体二分 . 平衡树 . LCT . 字符串 . 随 ...
- 一本通提高篇——斜率优化DP
斜率优化DP:DP的一种优化形式,主要用于优化如下形式的DP f[i]=f[j]+x[i]*x[j]+... 学习可以参考下面的博客: https://www.cnblogs.com/Xing-Lin ...
- 信息学奥赛一本通 提高篇 序列第k个数 及 快速幂
我是传送门 这个题首先是先判断是等差还是等比数列 等差的话非常简单: 前后两个数是等差的,举个栗子: 3 6 9 12 这几个数,(我感觉 1 2 3 4并说明不了什么) 每次都加3嘛,很容易看出,第 ...
- P2512 【一本通提高篇贪心】「一本通 1.1 练习 6」[HAOI2008]糖果传递
[HAOI2008]糖果传递 题目描述 有 n n n 个小朋友坐成一圈,每人有 a i a_i ai 个糖果.每人只能给左右两人传递糖果.每人每次传递一个糖果代价为 1 1 1. 输入格式 小朋友 ...
- 提高Web性能的前端优化技巧总结
- 提高篇(1):RMQ问题与ST表
RMQ是英文Range Minimum/Maximum Query的缩写,是询问某个区间内的最值,这里讲一种解法:ST算法 ST算法通常用在要多次(10^6级别)询问区间最值的问题中,相比于线段树,它 ...
- 【双向广搜+逆序数优化】【HDU1043】【八数码】
HDU上的八数码 数据强的一B 首先:双向广搜 先处理正向搜索,再处理反向搜索,直至中途相遇 visit 和 队列都是独立的. 可以用一个过程来完成这2个操作,减少代码量.(一般还要个深度数组) 优化 ...
- Secret Milking Machine POJ - 2455 网络流(Dinic算法---广搜判断+深搜增广)+时间优化+二分
题意: 第一行输入N M C ,表示从1到N有M条无向边,现在要从1走到N 走C次完全不同的路径,求最长边的最小值.下面M行是从a点到b点的距离. 建图: 题上说从两点之间可以有多条边,问的是从1~N ...
- Java提高篇(三三)-----Map总结
在前面LZ详细介绍了HashMap.HashTable.TreeMap的实现方法,从数据结构.实现原理.源码分析三个方面进行阐述,对这个三个类应该有了比较清晰的了解,下面LZ就Map做一个简单的总结. ...
随机推荐
- SpringBoot从0到0.7——第一天
SpringBoot从0到0.7--第一天 学习的第一步当然是收拾好心情,先把环境搭建起来,写出第一个helloword出来. 第一步:安装IDEA和Tomcat 我安装的是IDEA 2021.2.2 ...
- docker使用详解
一.docker简介 docker 是一个开源的应用容器引擎,docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化 ...
- 基础路径规划算法(Dijikstra、A*、D*)总结
引言 在一张固定地图上选择一条路径,当存在多条可选的路径之时,需要选择代价最小的那条路径.我们称这类问题为最短路径的选择问题.解决这个问题最经典的算法为Dijikstra算法,其通过贪心选择的步骤从源 ...
- zabbix 1.1
1.zabbix监控平台 2.zabbix的三部分组件: Zabbix server 是 Zabbix软件的核心组件,agent 向其报告可用性.系统完整性信息和统计信息.server也是存 ...
- 152-技巧-Power Query 快速合并文件夹中表格之自定义函数 TableXlsxCsv
152-技巧-Power Query 快速合并文件夹中表格之自定义函数 TableXlsxCsv 附件下载地址:https://jiaopengzi.com/2602.html 一.背景 在我们使用 ...
- SpringBoot 整合 RabbitMQ 实现消息可靠传输
消息的可靠传输是面试必问的问题之一,保证消息的可靠传输主要在生产端开启 comfirm 模式,RabbitMQ 开启持久化,消费端关闭自动 ack 模式. 环境配置 SpringBoot 整合 Rab ...
- ABP框架之——数据访问基础架构
大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享阅读心得,希望我的文章能成为你成长路上的一块垫脚石,我们一起精进. 几乎所有的业务应用程序都要适用一种数据库基础架构,用来实现数据访问逻辑,以便从数 ...
- 支付宝开放平台--网页&移动应用(一)
前提是先在支付宝上签约自己需要的支付宝功能,然后支付宝开放平台才能设置你需要的功能 一:支付宝开放平台登录 登录进入支付宝开放平台 二:根据自己的需求创建应用(我是用的网页&移动应用) 三:点 ...
- 【主流技术】ElasticSearch 在 Spring 项目中的实践
前言 ElasticSearch简称es,是一个开源的高扩展的分布式全文检索引擎. 它可以近乎实时的存储.检索数据,其扩展性很好,ElasticSearch是企业级应用中较为常见的技术. 下面和大家分 ...
- 实现领域驱动设计 - 使用ABP框架 - 通用准则
在进入细节之前,让我们看看一些总体的 DDD 原则 数据库提供者 / ORM 无关性 领域和应用程序层应该与 ORM / 数据库提供程序 无关.它们应该只依赖于 Repository 接口,而 Rep ...