思路
对于每一行,指派一个哈希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;
}
};