剑指Offer 13-16题解

剑指Offer 13-16题解

目前我做过的剑指Offer…

剑指Offer 13. 机器人的运动范围

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

来源:力扣(LeetCode)

分析 :

从起点【0,0】开始走能走的全部格子个数 <可采用DFS BFS>
其中约数有 格子i , j 的位数和不能大于 k <剪枝>

方法一 :DFS+剪枝

建立一个代表格子的二维数组vertor<vector<bool>>
从起点开始DFS, 遍历后标记 已进入即 false->true
一个计算数位和的函数
剪枝条件为 越过数组边界 + 数位和 > k

代码 : 时间(nm) 空间(nm)

class Solution {
public:
    int movingCount(int m, int n, int k) {
        vector<vector<bool>> visited(m,vector(n,false)); //创建数组表示此空间
        int vi=visited.size(),vj=visited[0].size();     //计算空间边界
        return DFS(visited,0,0,vi,vj,k);
    }
    int  DFS(vector<vector<bool>> &visited,int i,int j,int vi,int vj,int k)
    {   //i,j, 代表起点 ,vi,vj,边界  剪枝参数
        if(i<0||i>=vi||j<0||j>=vj||visited[i][j]||weishuhe(i,j)>k)  //剪枝判断条件
            return 0;
        visited[i][j]=true;     //标记已进入
        return 1+DFS(visited,i+1,j,vi,vj,k)+DFS(visited,i-1,j,vi,vj,k)+DFS(visited,i,j-1,vi,vj,k)+DFS(visited,i,j+1,vi,vj,k);    //返回当前格子 + 接下来的可行格子    
    }
    int weishuhe(int i,int j)
    {
        int sum=0;
        while(i)   //i各位数和
        {
            sum+=i%10;
            i=i/10;
        }
        while(j)  //j各位数和
        {
            sum+=j%10;
            j=j/10;
        }
        return sum;
    }
};
方法二: BFS +剪枝

建立一个代表格子的二维数组vertor<vector<bool>>
创建一个队列 辅助实现BFS
剪枝条件不变 越界 + 位数和 > k

代码:时间(nm) 空间(nm)

class Solution {
public:
    int movingCount(int m, int n, int k) {
     	//开辟一个m,n数组表示格子
	vector<vector<bool>> visited(m,vector(n,false));
     	//辅助队列q
    queue<vector<int>> q;
        //初值 i,j,i的位数和,j的位数和
    q.push({0,0,0,0});
        //读取队首数组
    vector<int> x;
        //记录可进入的格子数
    int s=0;
        //遍历条件 队列不空
    while(q.size()>0)
    {
        x=q.front();
        q.pop();
        //剪枝
        if(x[0]>m-1||x[1]>n-1||x[2]+x[3]>k||visited[x[0]][x[1]])
        continue;
        visited[x[0]][x[1]]=true;
        ++s;
        //入队  这里位数和用的数学规律 进位 位数和会减少8 不进位则加1
        q.push({x[0]+1,x[1],(x[0]+1)%10==0?x[2]-8:x[2]+1,x[3]});
        q.push({x[0],x[1]+1,x[2],(x[1]+1)%10==0?x[3]-8:x[3]+1});
    }
    return s;
    }

剑指Offer 14-l. 剪绳子

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

来源:力扣(LeetCode)

分析:

可采用动态规划的思想 :
可贪心思想:
可数学方法:

方法一: 动态规划 时间(n^2) 空间(n)

先看看是不是长度小于4的特殊情况(因为必须剪成至少2段)
列举出长度n为1,2,3的剪法的最大积 (不要求必须剪成至少两段)(因为是子段,实际绳子已经是大于等于两段了)
当长度 n 增加的过程中,切割成两段,并记录这两段绳子剪发最大积的积,遍历每种可能取最大值为长度n的剪法最大积
累加循环至所需值

class Solution {
public:
    //判断那个数更大
    int max(int a,int b)
    {
        return a>b? a:b;
    }
    int cuttingRope(int n) {
        if(n<4)		//是否是特殊情况
        return n-1;
        //初始化数组
        vector<int> dp(n+1);
        //列出其1,2,3的最大剪积
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;
        //开始动态规划
        for(int i=4;i<=n;++i)
        {
            for(int j=1;j<=i/2;j++)
            {
                //将其分为 (i-j) 和 j 两段
                //找出两段((i-j) ,j )的最大剪积(dp[i-j] ,dp[j])的 最大积
                dp[i]=max(dp[i],dp[j]*dp[i-j]);
            }
        }
        return dp[n];
    }
};
方法二 : 贪心算法 时间(n) 空间(1)

一般情况 在 (n>4) 时,分两段的积 > 不分, 分三段的积>分两段
所以在长不小于四的情况下尽量多分出3,为什么是3呢,因为大于3可再分且再分的积更大
特殊情况 (n<4) 不分反而更大,若没有经历上一步则分出一个1(去满足必须分一段以上)

class Solution {
public:
    int cuttingRope(int n) {
        //小于4且未分过,结果分出一个1
        if(n<4)
        return n-1;
        int i=1;
        //大于4的情况 尽量多分
        while(n>4)
        {
            n-=3;
            //每分一次
            //总积多×3
            i*=3;
        }
        return i*n;
    }
};
方法三:数学方法

数学方法很厉害,我就不介绍了,我们应该更注重思维
算出来的结果就如贪心得出来的一样,我就不介绍了

代码:

 class Solution {
public:
    int cuttingRope(int n) {
       if(n<4)//小于4 情况
         return  n-1;
        //大于4的情况
       int a=n/3,b=n%3;
       if(b==0)
         return pow(3,a);
       if(b==1)
         return pow(3,a-1)*4;
       return pow(3,a)*b;
    }
};


剑指Offer 14-ll. 剪绳子

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。来源:力扣(LeetCode)

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

来源:力扣(LeetCode)

​ 分析:和上题一样

此处因为数据量非常大,所以用取余操作
因为数据量巨大,所以动态规划也不太推荐,相比之下动态规划的时间和空间都会很大

方法一: 贪心 时间(n) 空间(1)

代码:

class Solution {
public:
    int cuttingRope(int n) {
        //小于4的情况
        if(n<4)
        return n-1;
        //用long 储存是因为1e9*3大于int 最大值 
        //也可用 unsign int 
        long int i=1; 
        int p=1e9+7;
        while(n>4)
        {
            n-=3;
            i=(i*3)%p;
        }
        return (i*n)%p;
    }
};

剑指Offer 15. 二进制中1的个数

请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。

来源:力扣(LeetCode)

思路:

除二 判断余数

位运算

方法一: 除二 判余 时间(n) 空间(1)

代码:

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int i=0;
        while(n)
        {
            //除二 判断余数
            if(n%2!=0)
                ++i;
            n/=2;
        }
        return i;
    }
};
方法二:位运算 时间(n) 空间(1)
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int i=0;
        while(n)
        {
            //n与1 与运算
            if(n&1==1)
            ++i;
            //n右移
            n=n>>1;
        }
        return i;
    }
};

剑指Offer 16. 数值的整数次方

实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。

来源:力扣(LeetCode)

思路:

暴力直接累成 (暴力都不推荐 且此题暴力超时)

二分法幂运算 优化

方法一 : 时间(log n) 空间(1)

优化累成
判断n的2进制数位是否为1 循环 x = x^2 n 右移

代码:

class Solution {
public:
    double myPow(double x, int n) {
        double result=1;
        //注意取值范围
        long l=n;
        //负数 取倒 取反
        if(l<0)
            {
                x=1/x;
                l=-l;
            }
        while(l)
        {
            //二进制位是否为一
           if(l&1)
            result*=x;
            //x变平方
            x*=x;
            //右移l
            l>>=1;
        }
        return result;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值