题意概述

  • 给定两个数字n,m
  • 每次对n进行一次操作,可对其自加,自减,平方
  • 问至少多少次操作可使n=m

方法一:BFS

思路与具体做法

  • 如果n>=m,n缩小只有通过自减来实现,直接返回n-m即可
  • 广度优先搜索,每次对队首元素分别进行自增,自减,平方后加入队列,同时对操作后的数继承队首元素深度值+1
  • 最后总的操作次数即dp[m]
class Solution {
public:
    int solve(int n, int m) {
        if(n>=m)return n-m;
        queue<int>q;
        map<int,int>d;//当前数字,深度
        q.push(n);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            if(u==m)return d[m];
            if(u<m){
                if(!d[u+1]){q.push(u+1);d[u+1]=d[u]+1;}
                if(!d[u*u]){q.push(u*u);d[u*u]=d[u]+1;}
            }
            if(!d[u-1]){q.push(u-1);d[u-1]=d[u]+1;}
        } 
        return d[m];
    }
};

时间复杂度和空间复杂度分析

时间复杂度:,最坏情况下,m>n,只能n不断自增
空间复杂度:,最下情况下用到了长度为m-n的队列和map映射

方法二:DFS

思路与具体做法

  • 如果n>=m,n缩小只有通过自减来实现,直接返回n-m即可
  • 因为平方是增长最快的方式,所以优先平方操作,我们找n^k<=m<=n^(k+1)或sqrt(m)<=n<=sqrt(m)+1,并比较n平方后的左右值哪一个更加接近m或m开方后的左右值哪一个跟加接近n
  • 此时就不需再有平方操作了,仅能通过n自增自减来实现向m的逼近
    接着不断递归直到n==m即可

图片说明

class Solution {
public:
    int solve(int n, int m) {
        if(n>=m)return n-m;//n>=m时,变小方式只有n自减 
        int k=sqrt(m);//对m开方,操作步骤+1,且k略小于m开方 
        if(m-k*k>(k+1)*(k+1)-m){//k从左右两个方向逼近m,选取距离最近的那一个 
            k++;
        }
        int ans=abs(m-k*k);//此时已不能再平方,只能m自增或自减,步骤+abs(m-k*k) 
        return min(m-n,solve(n,k)+ans+1);//向下递归
    }
};

时间复杂度和空间复杂度分析

时间复杂度:,最坏情况下,m>n,只能n不断自增
空间复杂度:,最坏情况下,需要m-n深度的递归栈