• 剑指offer专项突击版第34天


    剑指 Offer II 100. 三角形中最小路径之和

    经典线性dp

    class Solution {
    public:
        int minimumTotal(vector<vector<int>>& triangle) {
            int n = triangle.size();
            for(int i = 1; i < n; i++) { //处理边界,以防stack overflow
                triangle[i][0] += triangle[i-1][0];
                triangle[i][i] += triangle[i-1][i-1];
            }
    
            for(int i = 2; i < n; i++) 
                for(int j = 1; j < i; j++)
                    triangle[i][j] += min(triangle[i-1][j],triangle[i-1][j-1]); 
    
            int res = 0x3f3f3f3f;
            for(int i = 0; i < n; i++) res = min(res,triangle[n-1][i]);
            return res;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    剑指 Offer II 101. 分割等和子集

    1. 容易想到若 n u m s nums nums 的总和为奇数,或最大数大于和的一半( t a r g e t target target),一定为 f a l s e false false
    2. 若总和为偶数,接下来就是找是否有子序列的和为 t a r g e t target target,这里就需要用到 D P DP DP 方程 f [ i ] ∣ = f [ i − n u m ] f[i] \quad |= \quad f[i-num] f[i]=f[inum] ,其中 f [ i ] f[i] f[i] 表示 i i i 是否存在。
    class Solution {
    public:
        bool canPartition(vector<int>& nums) {
            int n = nums.size();
            int total = 0, maxx = -0x3f3f3f3f;
            for(const int &num: nums) {
                total += num;
                maxx = max(maxx,num);
            }
            if(total % 2 || total/2 < maxx) return false;
            int target = total / 2;
            vector<int> f(target+1, 0);
            f[0] = true;
            for(int i = 0; i < n; i++) {
                int num = nums[i]; //将nums[i] 换成num速度快了一倍。。
                for(int j = target; j >= num; j--)
                    f[j] |= f[j-num];
            }
                
            return f[target];            
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    剑指 Offer II 102. 加减的目标值

    1. n e g neg neg 表示添加 − - 的整数之和, s u m sum sum n u m s nums nums 所有数之和,那么 ( s u m − n e g ) − n e g (sum-neg)-neg (sumneg)neg 则为添加 + + + 的整数之和。
    2. 那么可以推导出: ( s u m − n e g ) − n e g = s u m − 2 ∗ n e g = t a r g e t (sum-neg)-neg = sum - 2 * neg = target (sumneg)neg=sum2neg=target。进而推导出: n e g = ( s u m − t a r g e t ) / 2 neg = (sum-target) / 2 neg=(sumtarget)/2
    3. 所以发现若 n u m s nums nums 存在子序列之和为 n e g neg neg 那么就可以构造结果为 t a r g e t target target 的表达式。问题进而转化成:找出 n u m s nums nums 的子序列之和为 n e g neg neg 的子序列总数。
    4. 这个问题由 D P DP DP 来解决,设 f [ i ] [ j ] f[i][j] f[i][j] n u m s nums nums i i i 个元素和为 j j j 的子序列个数。
    class Solution {
    public:
        int findTargetSumWays(vector<int>& nums, int target) {
            int total = 0, n = nums.size();
            for(int num: nums) total += num;
            int diff = total - target;
            if(diff < 0 || diff % 2) return 0; //若target大于total 或 diff为奇数则直接返回
            int neg = diff / 2;  //neg = (total - target) / 2
            vector<vector<int>> f(n+1, vector<int>(neg+1, 0));
            f[0][0] = 1;
            for(int i = 1; i <= n; i++) {
                int num = nums[i-1];
                for(int j = 0; j <= neg; j++) {
                    if(j < num) {
                        f[i][j] = f[i-1][j];
                    } else {
                        f[i][j] = f[i-1][j] + f[i-1][j-num];
                    }
                }
            }
    
            return f[n][neg];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    优化成一维空间

    class Solution {
    public:
        int findTargetSumWays(vector<int>& nums, int target) {
            int total = 0, n = nums.size();
            for(int num: nums) total += num;
            int diff = total - target;
            if(diff < 0 || diff % 2) return 0; 
            int neg = diff / 2;
            vector<int> f(neg+1, 0);
            f[0] = 1;
            for(int i = 1; i <= n; i++) {
                int num = nums[i-1];
                for(int j = neg; j >= num; j--) {
                    f[j] += f[j-num];
                }
            }
    
            return f[neg];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    java aspose cells 读取名称管理器
    动态规划基础
    Oracle启动报错解决:ora-00119和ora-00132
    vue自定义组件 事件修饰符 表单修饰符
    机器学习各个算法的优缺点!(下篇) 建议收藏。
    面试-Redis常见场景
    [old]TeamDev DotNetBrowser Crack
    《操作系统-真象还原》10. 输入输出系统
    【flink sql & table api】时间属性的指定与使用注意事项
    某大厂软件测试岗一面笔试题+二面问答题面试经验分享
  • 原文地址:https://blog.csdn.net/hys__handsome/article/details/126407700