力扣1981——最小化目标值与所选元素的差(哈希表+动态规划)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路

对于每一行,指派一个哈希set存所有到当前行为止的可取总和的值,下一行的dp set中的元素就是该行每个元素加上上一行dp set的每个元素。
最后一行对应的set即为每行选一个的情况下,选到最后一行的所有可能的元素之和,它和target的差值的最小值就是答案,因为每次dp set更新的时候只用到上一次的结果,所有可以只用两个哈希set压缩空间。
但是,直接这么写会超时。

//超时
class Solution {
public:
    int minimizeTheDifference(vector<vector<int>>& mat, int target) {
        int m = mat.size();
        int n = mat[0].size();
        unordered_set<int> dp;    
        for(int j = 0; j < n; j++) {
            dp.insert(mat[0][j]);
        }    
        for(int i = 1; i < m; i++) {
            unordered_set<int> tmp;
            for(auto y : mat[i]) {
                for(auto x : dp) {
                    tmp.insert(x + y);
                }
            }
            dp = move(tmp);
        }
        int ans = 0x0F0F0F0F;
        for(auto x : dp) {
            ans = min(ans, abs(x - target));
        }
        return ans;
    }
};

仔细观察发现,在更新过程,往新的set加入元素的过程中,有很多不必要操作,如果要加入的和已经大于target了,再往后只会越来越大,离target越来越远,不会是答案;而当先小于target的元素和,在下一行时是有希望离target更近的,应该加入set。这里相当于剪枝了。

最后,目标值只会有两种情况,由小于等于target的和产生,或者由大于target的和产生;
小于等于target的解可以从最后一行的dp set中遍历得出;
对于大于target的情况,需要单独一个变量minbig记录比target大的和中的最小值,在每一行的遍历中,由上一行的minbig加本行最小值为初始值,如果在本行产生新的超过target的和,就更新minbig。
最后两种情况下最小值就是答案。

代码

class Solution {
public:
    int minimizeTheDifference(vector<vector<int>>& mat, int target) {
        int m = mat.size();
        int n = mat[0].size();
        unordered_set<int> dp; 
        unordered_set<int> tmp;
        int minbig = 0x0F0F0F0F;   
        for(auto x : mat[0]) 
            dp.insert(x); 
        for(int i = 1; i < m; i++) {
            minbig += *min_element(mat[i].begin(), mat[i].end()); 
            for(auto y : mat[i]) {
                for(auto x : dp) {
                    if(x + y <= target) tmp.insert(x + y);
                    else minbig = min(minbig, x + y);
                }
            }
            dp = tmp;
            tmp.clear();
        }
        int ans = minbig - target;
        for(auto x : dp) 
            ans = min(ans, abs(x - target));
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值