P4289 【一本通提高篇广搜的优化技巧】[HAOI2008]移动玩具

使用BFS解决HAOI2008移动玩具问题的优化技巧
博客介绍了如何利用BFS(广度优先搜索)解决一个4x4方格中玩具移动的最少步数问题。通过对初始和目标状态进行二进制与十进制转换,结合变量、队列和边界判断,实现状态的扩展与搜索,最终找到最短移动路径。

[HAOI2008]移动玩具

题目描述

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

输入格式

444 行表示玩具的初始状态,每行 444 个数字 1\texttt{1}10\texttt{0}01\texttt{1}1 表示方格中放置了玩具,0\texttt{0}0 表示没有放置玩具。接着是一个空行。接下来 444 行表示玩具的目标状态,每行 444 个数字 1\texttt{1}10\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_ 将扩展节

### P1463 POI2001 HAOI2007 反素数 C语言实现 #### 定义与性质 对于给定的数据范围 \( 1 \leq N \leq 2 \times 10^9 \),反素数是指某个正整数 \( x \) 满足条件:对于所有的 \( 0 < i < x \),都有 \( g(x) > g(i) \)[^2]。这里 \( g(n) \) 表示 \( n \) 的约数个数。 #### 解决方案概述 为了高效求解此问题,可以采用预处理加深度优先索 (DFS) 或广度优先索 (BFS) 来枚举可能的结果,并过剪枝优化来减少不必要的计算量[^1]。 #### 关键算法思路 核心在于利用质因数分解特性以及贪心策略,在遍历过程中尽可能早地排除不可能成为最优解的情况: - 使用 DFS 枚举所有小于等于最大允许因子数量的组合; - 对于每一个新的乘积项,检查其是否超出界限并更新当前最佳答案; - 剪枝原则包括但不限于:当已知更优解存在时不继续深入探索;控制各位置上的指数上限以防止重复计数等。 #### C语言代码实现 以下是基于上述原理的一个简化版C语言程序框架: ```c #include <stdio.h> #define MAX_PRIME 10 // 考虑前几个最小的质数即可覆盖大部分情况 int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; // 预定义的小质数表 long long best_ans; void dfs(int pos, int limit, long long product, int divisor_count){ if(pos >= MAX_PRIME || product * primes[pos] > best_ans){return;} for(int exp=1;exp<=limit && product*primes[pos]<=best_ans;exp++){ product *= primes[pos]; if(product>best_ans&&divisor_count*(exp+1)>max_divisors){ max_divisors=divisor_count*(exp+1); best_ans=product; } dfs(pos+1, exp, product, divisor_count*(exp+1)); } } // 主函数部分省略... ``` 该片段展示了如何构建一个递归过程来进行有效索,并适时调整全局变量 `best_ans` 和 `max_divisors` 记录目前发现的最佳候选者及其对应的除数数目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lvshu · 绿树

非常感谢您的搭讪

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值