最近刷题有点恶心了,应该是低效思考时间过多,时间跨度大但是效果低下导致的惩罚效应。想题目的时候尽量保持好的状态。
322. Coin Change
思路一
没有很好的分辨出暴力法和bottom-up在复杂度上的差异,使用递归强行暴力,超时。其中还有想过用贪婪算法,尽可能使用较大的货币,但后来给出反例[1, 3, 11, 12] 15导致贪心失败。
复杂度差异:暴力方法无法知道分解到当前步骤是否已经说明不能继续分解了,还是会继续循环递归直到最后;自下而上则在每一次分解之后都可以明确知道是否已经走了死路。举例说明[3, 11, 12] 15, 当15 = 11 + 4时,4已经明确不能分解,而暴力法会继续4 = 3 +1,到1的时候才放弃。
代码
class Solution {
public:
int coinCombine(vector<int>& coins, int amount) {
if (coins.size() == 1) {
if (amount % coins[0] == 0) return amount / coins[0];
else return -1;
}
vector<int> vec(coins.begin(), coins.end()-1);
int result = INT_MAX;
for (int i = amount / coins.back(); i >= 0 ; i--) {
int ret = coinCombine(vec, amount - i * coins.back());
if (ret == -1) continue;
result = min(result, ret + i);
}
if (result == INT_MAX) return -1;
return result;
}
int coinChange(vector<int>& coins, int amount) {
sort(coins.begin(), coins.end());
return coinCombine(coins, amount);
}
};
思路二
典型的自下而上的策略。
代码
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(1, 0);
for (int i = 1; i <= amount; i++) {
int num = INT_MAX;
for (int j = 0; j < coins.size(); j++) {
if (i < coins[j] || dp[i - coins[j]] == -1) continue;
num = min(num, dp[i - coins[j]] + 1);
}
if (num == INT_MAX) dp.push_back(-1);
else dp.push_back(num);
}
return dp.back();
}
};
221. Maximal Square
思路一
每一次只考虑将原来的矩阵向右下增加一行一列,状态保存为符合要求的方向左上角点坐标。
过程中犯了几个错误:
1. 循环中做判断的变量expand
,没有在每一次循环开始前重新初始化;
2. 边界问题(即第一行)一开始认为可以通过循环条件hold住,没有考虑y_len
在输入为[]
时的特殊情况
代码
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
if (matrix.size() == 0) return 0;
vector<pair<int, int>> point;
int x_len = matrix.size(), y_len = matrix[0].size();
for (int x = 0; x < x_len; x++) {
for (int y = 0; y < y_len; y++) {
point.push_back(pair<int, int>(x, y));
}
}
for (int l = 1; l <= min(x_len, y_len); l++) {
vector<pair<int, int>> v;
for (pair<int, int> p: point) {
bool expand = true;
if (p.first+l-1 < x_len && p.second+l-1 < y_len) {
for (int x = p.first; x < p.first+l && expand; x++) {
if (matrix[x][p.second+l-1] == '0') expand = false;
}
for (int y = p.second; y < p.second+l && expand; y++) {
if (matrix[p.first+l-1][y] == '0') expand = false;
}
if (expand) v.push_back(p);
}
}
point = v;
if (point.size() == 0) return (l-1)*(l-1);
}
return min(x_len, y_len) *min(x_len, y_len);
}
};
思路二
写完之后总觉得过于繁琐了,查了一下别人的做法果然惊艳到。维护一个二维数组
dp
,其中dp[i][j]
代表以(i, j)
作为正方形右下角中可以允许的最大边长。不仅代码简洁,逻辑清楚,而且复杂度也低。暂时不贴代码,等一天后自己再写。