剑指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;
    }
};

剑指 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];            
    }
};

剑指 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];
    }
};

优化成一维空间

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];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值