牛客网 106 J-Various Tree (BFS复习)

题目链接

题目

题目描述
It’s universally acknowledged that there’re innumerable trees in the campus of HUST.

And there are many different types of trees in HUST, each of which has a number represent its type. The doctors of biology in HUST find 4 different ways to change the tree’s type x into a new type y:

  1. y=x+1

  2. y=x-1

  3. y=x+f(x)

  4. y=x-f(x)

The function f(x) is defined as the number of 1 in x in binary representation. For example, f(1)=1, f(2)=1, f(3)=2, f(10)=2.
Now the doctors are given a tree of the type A. The doctors want to change its type into B. Because each step will cost a huge amount of money, you need to help them figure out the minimum steps to change the type of the tree into B.

Remember the type number should always be a natural number (0 included).
输入描述:

One line with two integers A and B, the init type and the target type.

输出描述:

You need to print a integer representing the minimum steps.

示例1
输入

5 12

输出

3

说明

The minimum steps they should take: 5->7->10->12. Thus the answer is 3.

分析

【题意】
给出起点 A 和终点B,每次可以对 A 进行四种操作

  • A=A+1

    • A=A1
    • A=A+f(A)
    • A=Af(A)
    • AB 需要的最少操作次数
      【思路】
      每种状态都有四个可以转移的方向,而且由于小的数可能有更多的1,向前搜索也是有必要的,所以四个搜索的方向都要考虑。

      不妨放在图上思考:含 n 个点,4n条边的有向图,求从A出发到B的最短路径。咋一看,往四个方向搜索会产生指数级的新状态,但其实总体来看,一共只有1e6个点,最坏的情况也不过是把所有的点都搜索一遍而已,由于是无权图求最短路径,直接上BFS搜索即可。
      【注意】
      虽然说最坏的情况要搜索所有的点一遍,但这还是要建立在剪枝的基础上:搜索过的点一定不能重复搜索,不然复杂度会炸(不要问我为什么知道)

      代码

      #include<stdio.h>
      #include<queue>
      using std::queue;
      int a, b;
      
      //预处理1~n每个十进制数含有的1的个数
      int bo[1000006] = { 0 };
      int f(int x) {
          if (x <= 0)return 0;
          if (bo[x] > 0)return bo[x];
          int temp = x / 2;
          return bo[x] = (f(temp) + x % 2);
      }
      
      //BFS辅助队列
      queue<int> help;
      int aim;
      //vis数组记录搜索过的状态,防止在环上反复搜索,把需要搜索的状态总数降低到n个
      int vis[1000006] = { 0 };
      //bfs的非递归写法
      int bfs(int cur,int cost) {
          int sz;
          int temp;
          while (!help.empty())
          {
              sz = help.size();
              for (int i = 0; i < sz; ++i) {
                  temp = help.front(); help.pop();
                  if (vis[temp] == 1)continue;
                  else vis[temp] = 1;
                  //遇到终点,结束搜索
                  if (temp == aim)return cost;
                  //加入下一次搜索的四个新状态
                  help.push(temp - 1);
                  help.push(temp + 1);
                  help.push(temp - f(temp));
                  help.push(temp +f(temp));
              }
              ++cost;
          }
          return -1;//没找到,理论上是不可能的(重复+1/-1一定能得到B),但为了语法上的正确添加一个返回值
      }
      
      int main() {
          scanf("%d %d", &a, &b);
          help.push(a);
          aim = b;
          b = bfs(a, 0);
          printf("%d", b);
      }
### 问题分析 在火山爆发后安全区域的计算问题中,目标是找出所有未被岩浆覆盖的方块数量。岩浆从火山口开始,向四周扩散,但只能流向海拔更低的区域。因此,问题可以转化为一个典型的图遍历问题,其中岩浆的流动路径类似于广度优先搜索(BFS)的扩展过程。 BFS 是一种经典的算法,适用于此类问题。CSP-J(计算机软件能力认证入门组)中常见的 BFS 算法可以有效处理网格上的连通区域问题,例如洪水填充、迷宫搜索等。通过 BFS 遍历,可以标记所有岩浆可以到达的格子,最终统计未被标记的格子数量即可得出安全区域的大小。 ### 算法实现思路 1. **初始化**:将火山口位置加入队列,并标记为已访问。 2. **BFS 遍历**:从队列中取出一个位置,检查其四个方向(上下左右)的相邻格子。如果相邻格子的海拔低于当前格子且未被访问过,则将其加入队列并标记为已访问。 3. **统计结果**:遍历结束后,所有未被访问的格子即为安全区域。 ### 代码示例 以下为使用 BFS 算法计算安全区域的 C++ 示例代码: ```cpp #include <iostream> #include <queue> using namespace std; const int MAXN = 1005; int h[MAXN][MAXN]; // 海拔数组 bool visited[MAXN][MAXN]; // 访问标记数组 int n, a, b; int dx[] = {-1, 1, 0, 0}; // 上下左右方向 int dy[] = {0, 0, -1, 1}; struct Point { int x, y; }; void bfs() { queue<Point> q; q.push({a, b}); visited[a][b] = true; while (!q.empty()) { Point cur = q.front(); q.pop(); for (int i = 0; i < 4; ++i) { int nx = cur.x + dx[i]; int ny = cur.y + dy[i]; if (nx >= 1 && nx <= n && ny >= 1 && ny <= n && !visited[nx][ny] && h[nx][ny] < h[cur.x][cur.y]) { visited[nx][ny] = true; q.push({nx, ny}); } } } } int main() { cin >> n >> a >> b; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { cin >> h[i][j]; } } bfs(); int safeArea = 0; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { if (!visited[i][j]) { ++safeArea; } } } cout << safeArea << endl; return 0; } ``` ### 复杂度分析 - **时间复杂度**:$ O(n^2) $,每个格子最多被访问一次。 - **空间复杂度**:$ O(n^2) $,用于存储访问标记和队列。 ### 注意事项 - 输入数据中的行和列编号从 1 开始,因此需要确保数组索引与之匹配。 - 火山口的海拔可能不是整个地图的最高点,因此必须严格按照 BFS 的规则进行扩展。 - 若多个火山口存在,可将所有起点同时加入队列进行扩展。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值