leetcode剑指offer2

这篇博客探讨了四个编程题目,包括矩阵中的路径寻找、机器人在有限区域内的运动范围计算、以及两种剪绳子以最大化乘积的方法。通过动态规划、贪心算法和深度优先搜索等技术解决这些问题,强调了在解决这类问题时的剪枝技巧和时间复杂度优化。
摘要由CSDN通过智能技术生成

剑指 Offer 12. 矩阵中的路径

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

例如,在下面的 3×4 的矩阵中包含单词 “ABCCED”(单词中的字母已标出)。

提示:

1 <= board.length <= 200
1 <= board[i].length <= 200
board 和 word 仅由大小写英文字母组成

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof

class Solution {
public:
    bool vis[201][201];
    int m,n;
    int length;
    int dx[4]={1,-1,0,0};
    int dy[4]={0,0,1,-1};
    bool flag;

    bool isOk(int x,int y){
        if(x<0||x>=m)return 0;
        if(y<0||y>=n)return 0;
        return vis[x][y]==0;
    }

    void dfs(int x,int y,int dep,vector<vector<char>>& board, string word){
        if(dep>=length){
            flag=1;
            return;
        }
        if(flag)return;///!!!!
        for(int i=0;i<4;i++){
            int nx=x+dx[i];
            int ny=y+dy[i];
            if(!isOk(nx,ny))continue;
            if(board[nx][ny]==word[dep]){
                vis[nx][ny]=1;
                dfs(nx,ny,dep+1,board,word);
                vis[nx][ny]=0;
            }
        }
    }

    bool exist(vector<vector<char>>& board, string word) {
        m=board.size(), n=board[0].size();
        length=word.length();
        flag=0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(board[i][j]==word[0]){
                    vis[i][j]=1;
                    dfs(i,j,1,board,word);
                    vis[i][j]=0;
                }
                if(flag)return 1;
            }
        }
        return 0;

    }
};
  1. dfs搜索,对4个方向进行搜索
  2. 注意剪枝,找到即返回,if(flag)return,否则会超时
  3. 时间复杂度 O(3^K MN)
    方案数计算: 设字符串长度为 K ,搜索中每个字符有上、下、左、右四个方向可以选择,舍弃回头(上个字符)的方向,剩下 3 种选择,因此方案数的复杂度为 O(3^K)
  4. vector<vector> vis(m,vector(n,false));//记录访没访问过

剑指 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。请问该机器人能够到达多少个格子?

class Solution {
private:
    int dx[4]={1,-1,0,0};
    int dy[4]={0,0,1,-1};
    int num;
    
    int count(int x){
        int res=0;
        while(x>0){
            res+=x%10;
            x=x/10;
        }
        return res;
    }

    void dfs(int x,int y,int m,int n,int k,vector<vector<bool>> &vis){
        if(x<0||x>=m||y<0||y>=n||vis[x][y])return;
        if(count(x)+count(y)>k)return;
        vis[x][y]=1;
        num++;
        if(num>=m*n)return;

        for(int i=0;i<4;i++){
            int nx=x+dx[i];
            int ny=y+dy[i];
            dfs(nx,ny,m,n,k,vis);
        }
    }
    
public:
    int movingCount(int m, int n, int k) {
        vector<vector<bool>> vis(m,vector<bool>(n,0));
        num=0;
        dfs(0,0,m,n,k,vis);
        return num;
    }
};
  1. 和12题差不多,但是不需要将vis处理为false
  2. 时间复杂度O(mn)

剑指 Offer 14- I. 剪绳子

给你一根长度为 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)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof

class Solution {
public:
    int cuttingRope(int n) {
        int i,j;
        vector<int> dp(n+1,0);
        dp[1]=1;
        dp[2]=1;
        for(i=3;i<=n;i++){
            for(j=1;j<i;j++){
                dp[i]=max(dp[i],max(dp[j],j)*max(dp[i-j],i-j));
            }
        }
        return dp[n];
    }
};
  1. 动态规划,明显这题具有最优子结构,时间复杂的为O(n^2)
  2. 贪心① 当所有绳段长度相等时,乘积最大。② 最优的绳段长度为 3 。时间复杂度为O(n)

贪心算法:

int cuttingRope(int n) {
    return n <= 3? n - 1 : pow(3, n / 3) * 4 / (4 - n % 3);
}
class Solution:
    def cuttingRope(self, n: int) -> int:
        if n < 4:
            return n - 1
        res = 1
        while n > 4:
            res *=3
            n -= 3
        return res * n

作者:edelweisskoko
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/jian-zhi-offer-14-i-jian-sheng-zi-huan-s-xopj/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

步骤如下:

如果 n == 2,返回1,如果 n == 3,返回2,两个可以合并成n小于4的时候返回n - 1
如果 n == 4,返回4
如果 n > 4,分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3;最后返回时乘以小于等于4的最后一小段
以上2和3可以合并

剑指 Offer 14- II. 剪绳子 II

给你一根长度为 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。

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

2 <= n <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof

typedef long long ll;
class Solution {
public:
    ll mod=1e9+7;
    ll pow(int a,int n){
        ll res=1;
        if(n==0)return 1;
        if(n==1)return a;
        res=pow(a,n/2);
        res=res*res%mod;
        if(n&1)res=res*a%mod;
        return res%mod;
    }

    int cuttingRope(int n) {
        ll ans;
        if(n<4)ans=n-1;
        else{
            if(n%3==0)ans=pow(3,n/3);
            else if(n%3==1)ans=pow(3,n/3-1)*4%mod;
            else ans=pow(3,n/3)*2%mod;
        }
        return ans;
    }
};
  1. 数据范围扩大,还要余mod,本题涉及 “大数越界的求余问题” 。
  2. 使用贪心来写,时间复杂度O(logn)
  3. 动态规划取余会无法比较大小,当然可以最后取余

动态规划:

import java.math.BigInteger;
class Solution {
    public int cuttingRope(int n) {
        BigInteger[] dp = new BigInteger[n + 1];
         Arrays.fill(dp, BigInteger.valueOf(1));
        // dp[1] = BigInteger.valueOf(1);
        for(int i = 3; i < n + 1; i++){
            for(int j = 1; j < i; j++){
                dp[i] = dp[i].max(BigInteger.valueOf(j * (i - j))).max(dp[i - j].multiply(BigInteger.valueOf(j)));
            }
        }
        return dp[n].mod(BigInteger.valueOf(1000000007)).intValue();
    }
}

作者:ollieq
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/jian-dan-li-jie-dong-tai-gui-hua-xun-hua-4g3o/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

技巧:先暴力搜索先找规律

class Solution {
public:
    void dfs(int n, long sum, long multi, long& ans) {
        if (sum == n) {
            ans = max(ans, multi);
            return;
        } else if (sum > n) {
            return;
        }
        for (long i = 1; i < n; i++) {
            dfs(n, i+sum, i*multi, ans);
        }
        return;
    }

    int cuttingRope(int n) {
        long ans = 0;
        dfs(n, 0, 1, ans);
        return ans;
    }
};

//--->
n     乘积     子数字
2       1       1 1
3       2       1 2
4       4       2 2
5       6       2 3
6       9       3 3
7       12      2 2 3
8       18      2 3 3
9       27      3 3 3
10      36      2 2 3 3
11      54      2 3 3 3
12      81      3 3 3 3
13      108     2 2 3 3 3
14      162     2 3 3 3 3
15      243     3 3 3 3 3
16      324     2 2 3 3 3 3
17      486     2 3 3 3 3 3
18      729     3 3 3 3 3 3
19      972     2 2 3 3 3 3 3
20      1458    2 3 3 3 3 3 3
21      2187    3 3 3 3 3 3 3
22      2916    2 2 3 3 3 3 3 3
23      4374    2 3 3 3 3 3 3 3
24      6561    3 3 3 3 3 3 3 3
25      8748    2 2 3 3 3 3 3 3 3
26      13122   2 3 3 3 3 3 3 3 3
27      19683   3 3 3 3 3 3 3 3 3
28      26244   2 2 3 3 3 3 3 3 3 3
29      39366   2 3 3 3 3 3 3 3 3 3
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值