leetcode-周赛no.167-875 / 1534

本文探讨了链表转换为十进制数的算法,优化了寻找顺次数的策略,并介绍了利用二维前缀和解决矩阵最大边长问题的方法。

第一题

给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。

请你返回该链表所表示数字的 十进制值 。


  • 10min 1A
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    int getDecimalValue(ListNode* head) {
        vector<int> num;
        while(head->next!=nullptr){
            num.push_back(head->val);
            head = head->next;
        }
        num.push_back(head->val);
        
        vector<int> b;
        for(int i = num.size()-1;i>=0;i--){
            b.push_back(num[i]);
        }
        long res=0;
        long cnt=1;
        for(int i=0;i<b.size();i++){
            if(b[i]==1){
                res+=cnt;
            }
            cnt*=2;
        }
        return (int)res;
    }
};
  • 看了题解发现我是个傻逼
class Solution {
public:
    int getDecimalValue(ListNode* head) {
        int ret = 0;
        while (head != NULL) {
            ret = ret * 2 + head->val;
            head = head->next;
        }
        return ret;
    }
};
/*
作者:小白二号
链接:https://leetcode-cn.com/circle/discuss/mLljrr/view/GwXchV/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*/

第二题

我们定义「顺次数」为:每一位上的数字都比前一位上的数字大 1 的整数。

请你返回由 [low, high] 范围内所有顺次数组成的 有序 列表(从小到大排序)。

示例 1:

输出:low = 100, high = 300
输出:[123,234]


  • 超时代码
  • 测试样例[10,1e9],我傻逼了
    测试[10,1e7]花费9.5s
class Solution {
public:
    vector<int> sequentialDigits(int low, int high) {
        vector<int> res;
        for(int i = low;i<=high;i++){
            if(judge(i)){
                res.push_back(i);
            }
        }
        return res;
    }
    
    int judge(int num){
        vector<int> s;
        while(num/10>0){
            s.push_back(num%10);
            num/=10;
        }
        s.push_back(num);
        if(s.size()<=1){
            return 1;
        }
        int flag = 1;
        for(int i=0;i<s.size()-1;i++){
            if(s[i]!=s[i+1]+1){
                flag = 0;
                break;
            }
        }
        
        return flag;
    }
};
  • 优化了一下代码,不是暴力循环而是跳变循环
  • 还是超时
    测试[10,1e7]花费6.32s,不知道效率为何提升不大。
class Solution {
public:
    vector<int> sequentialDigits(int low, int high) {
        vector<int> res;
        for(int i = low;i<=high;){
            if(judge(i)){
                res.push_back(i);
                i+=je(i);
            }else{
                i++;
            }
        }
        return res;
    }
    
    int je(int num){
        int i = 0;
        while(num/((int)pow(10,(double)i))>10){
            i++;
        }
        return (int)pow(10,i);
    }
    
    int judge(int num){
        vector<int> s;
        while(num/10>0){
            s.push_back(num%10);
            num/=10;
        }
        s.push_back(num);
        if(s.size()<=1){
            return 1;
        }
        int flag = 1;
        for(int i=0;i<s.size()-1;i++){
            if(s[i]!=s[i+1]+1){
                flag = 0;
                break;
            }
        }
        
        return flag;
    }
};
  • 继续优化,把judge的暴力改成了log(n)的复杂度
    测试[10,1e7]花费0.1s,效率提升很大,但是还是tle
class Solution {
public:
    vector<int> sequentialDigits(int low, int high) {
        vector<int> res;
        for(int i = low;i<=high;){
            if(judge(i)){
                res.push_back(i);
                i+=je(i);
            }else{
                i++;
            }
        }
        return res;
    }
    
    int je(int num){
        int i = 0;
        while(num/((int)pow(10,(double)i))>10){
            i++;
        }
        return (int)pow(10,i);
    }
    
    int judge(int num){
        int pre = -1;
        while(num/10>=0){
            if(num==0){
                break;
            }
            if(pre == -1){
                pre = num%10;
                num/=10;
                continue;
            }else{
                if(pre != num%10+1){
                    return 0;
                } else{
                    pre = num%10;
                    num/=10;
                    continue;
                }
            }
        }
        return 1;
    }
};
  • 放弃了,比赛结束看题解
  • 简单的打表,我可能是个弱智
class Solution {
public:
    vector<int> sequentialDigits(int low, int high) {
        vector<int> table;
        vector<int> res;
        for(int i=1;i<=9;i++){
            int x = i;
            for(int j=i+1;j<=9;j++){
                x = x*10+j;
                table.push_back(x);
            }
        }
        sort(table.begin(),table.end());
        for(int i = 0;i<table.size();i++){
            if(table[i]>=low&&table[i]<=high){
                res.push_back(table[i]);
            }
        }
        return res;
    }
};

第三题

给你一个大小为 m x n 的矩阵 mat 和一个整数阈值 threshold。

请你返回元素总和小于或等于阈值的正方形区域的最大边长;如果没有这样的正方形区域,则返回 0 。
详细描述

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-side-length-of-a-square-with-sum-less-than-or-equal-to-threshold
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 不会,kankan题解
  • 二位前缀和
class Solution {
public:
    int maxSideLength(vector<vector<int>>& mat, int threshold) {
        long long map[305][305];
        memset(map,0,sizeof(map));
        int col = mat[0].size();
        int row = mat.size();
        for(int i=1;i<=row;i++){
            for(int j=1;j<=col;j++){
                map[i][j] = mat[i-1][j-1]+map[i-1][j]+map[i][j-1]-map[i-1][j-1];
            }
        }
        int maxx = 0;
        for(int i=1;i<=row;i++){
            for(int j=1;j<=col;j++){
                for(int k=1;k<=i&&k<=j;k++){
                    if((map[i][j]-map[i][j-k]-map[i-k][j]+map[i-k][j-k])<=threshold){
                        maxx = max(maxx,k);

                    }
                }
            }
        }
        return maxx;

    }
};
  • 改进思路,二位前缀和+二分

需要多次求子矩阵之和,且矩阵的值不会再次修改,应该使用二维前缀和矩阵预处理,提高求和效率
具体参考:
二维区域和检索 - 矩阵不可变

使用二分查找可以提高查找效率,不过大部分测试案例的结果都较小,靠近下限,C++用顺序查找不会造成超时,看评论python会超。

代码如下:

class Solution {
public:
    vector<vector<int>> Pre;//保存前缀和
    int len1;
    int len2;

    int Fun(int len) {//找到所有对应边长的正方形的最小总和
        if (len == 0) return 0;
        int Min = 0x7fffffff;
        for (int i = 0; i < len1 - len + 1; ++i) {
            for (int j = 0; j < len2 - len + 1; ++j) {
                int tmp = Pre[i + len][j + len] - Pre[i + len][j] - Pre[i][j + len] + Pre[i][j];
                if (tmp < Min) Min = tmp;
            }
        }
        return Min;
    }

    int maxSideLength(vector<vector<int>> &mat, int threshold) {
        len1 = mat.size();
        len2 = mat[0].size();
        Pre = vector<vector<int>>(len1 + 1, vector<int>(len2 + 1, 0));
        for (int i = 1; i <= len1; ++i) {
            for (int j = 1; j <= len2; ++j) {
                Pre[i][j] = Pre[i - 1][j] + Pre[i][j - 1] - Pre[i - 1][j - 1] + mat[i - 1][j - 1];
            }
        }
        int Min = min(len1, len2);
        //二分查找,一般答案靠近下限,所以顺序查找效率也不低
        /*
        for(int i=0;i<=Min;++i){
            if(Fun(i)>threshold) return i-1;
        }
        return Min;
        */

        int lo = 0;
        int hi = Min;
        while (lo < hi) {
            int mi = (lo + hi + 1) >> 1;
            int k = Fun(mi);
            if (k <= threshold) {
                lo = mi;
            } else hi = mi - 1;
        }
        return lo;

    }
};
/*
作者:jian-liao
链接:https://leetcode-cn.com/problems/maximum-side-length-of-a-square-with-sum-less-than-or-equal-to-threshold/solution/c-er-wei-qian-zhui-he-er-fen-cha-zhao-by-jian-liao/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*/
### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值