Reach a Number
题意
最开始数字是0,第i步可以+i或者-i,求最少需要步到达target
思路
首先如果target
是负数的话把他取绝对值一下,因为负数和正数是对称的,求绝对值后结果不变,这样我们就只考虑正数的情况
题目需要求的是?0 + ?1 + ?2 + ?3 + ... = target
,其中?
代表符号,可能是+
或者-
我们先把所有的?
看作+
,也就是一直往后累加直到累加和大于等于target
,累加和记作sum
这个时候,如果sum
等于target
,那么累加了多少次就是答案。关键是当sum
大于target
时,应该把哪些数字的符号变为负号(其实最少的情况是只需要把其中一个数字的符号变成负号就可以了,原因见下面)
容易发现,如果我们把某一个数字的符号变成-
(比如把i的符号变为-
),那么也就意味着sum
需要减去2i
,而且显然2i
是偶数,所以这里决定了sum - target
必须是偶数,所以当sum - target
不是偶数时,我们让sum
继续往后累加,直到sum - target
是偶数。这个时候再把(sum - target)/2
的符号变为-
就得到target
了
所以实质上就是把sum
一直累加,直到sum
大于target
并且sum - target
是偶数,累加了多少次答案就是多少
到这里这道题已经完美的解决了,不然可以求出步数,还可以求出具体的步骤
代码
class Solution {
public:
int reachNumber(int target) {
target = abs(target);
int step = 0;
long long cur = 0;
int i = 0;
for (i = 1; cur < target; ++i, ++step) {
cur += i;
}
while ((cur - target) & 1) {
cur += i;
++i;
++step;
}
return step;
}
};
复制代码
Pour Water
题意
把地面分成n分,从0到n-1标号,heights[i]
代表第i个地面的高度。然后在第K个地面上滴V滴水,水会往低处流,也就是水如果往heights[j]
流的话,必须heights[j] <= height[i]
。现在滴完水后,水会按照以下规则流动:
- 如果水流能向左流,并且向左流到最低点后,高度比
height[K]
小,那么向左流 - 如果水流不能向左流,则尝试向右流,同样的,向右流到最低点后,高度要比
height[K]
小 - 如果水流不能向左向右流,那就停在K这个位置上
每滴水流完之后停在哪里,哪里的高度就会+1,求出V滴水后每个地面的高度
思路
这题难就难在题意上,题意读懂后按照题目意思模拟就行了。
有一个trick就是,如果水能向左流,但是向左流了之后高度没变,那实际上会放弃左边,尝试向右流,举个栗子:[2, 2, 2, 2, 1], 1, 2
,水流最开始在2这个位置上,然后发现左边是可以流的,但是高度并不会变,所以会放弃左边尝试向右流,然后发现右边是可以流的并且高度比heights[K]
小,那么久向右流,最后答案是[2, 2, 2, 2, 2]
代码
class Solution {
public:
vector<int> pourWater(vector<int>& heights, int V, int K) {
if (heights.empty() || !V) {
return heights;
}
int n = heights.size();
for (int i = 1; i <= V; ++i) {
int left = K;
int left_min_idx = left;
while (left - 1 >= 0 && heights[left - 1] <= heights[left]) {
if (heights[left - 1] < heights[left]) {
left_min_idx = left - 1;
}
--left;
}
if (left_min_idx != K) {
++heights[left_min_idx];
continue;
}
int right = K;
int right_min_idx = right;
while (right + 1 < n && heights[right + 1] <= heights[right]) {
if (heights[right + 1] < heights[right]) {
right_min_idx = right + 1;
}
++right;
}
if (right_min_idx != K) {
++heights[right_min_idx];
continue;
}
++heights[K];
}
return heights;
}
};
复制代码
Pyramid Transition Matrix
题意
有一个类似于杨辉三角的金字塔三角形,给出了底部,还有一些规则。规则的格式是ABC
,C
的左儿子允许A
出现,C
的右儿子允许B
出现。根据底部和这些规则,求出能不能构造出一个金字塔三角形
思路
想到DP的做法,用dp[i][j][k]
表示第i行第j列能否放第k个字母
那么就枚举第i行第j列是字母几,然后判断左儿子和右儿子的状态能否达到
代码
class Solution {
public:
bool pyramidTransition(string bottom, vector<string>& allowed) {
for (const string& str : allowed) {
allow[str[2] - 'A'].emplace_back(make_pair(str[0] - 'A', str[1] - 'A'));
}
int depth = bottom.length();
for (int i = 1; i <= depth; ++i) {
dp[depth][i][bottom[i - 1] - 'A'] = true;
}
for (int i = depth - 1; i >= 1; --i) {
for (int j = 1; j <= i; ++j) {
for (int k = 0; k < 7; ++k) {
dp[i][j][k] = false;
for (size_t l = 0; l < allow[k].size(); ++l) {
int left = allow[k][l].first;
int right = allow[k][l].second;
dp[i][j][k] |= dp[i + 1][j][left] & dp[i + 1][j + 1][right];
if (dp[i][j][k]) {
break;
}
}
}
}
}
for (int i = 0; i < 7; ++i) {
if (dp[1][1][i]) {
return true;
}
}
return false;
}
private:
bool dp[110][110][7];
vector<pair<int, int>> allow[7];
};
复制代码
Set Intersection Size At Least Two
题意
有若干个区间[a, b](a < b)
,现在选出一个最小的整数集合,让这个集合和每一个区间的交集都大于等于2
思路
考虑贪心做法
我们先把区间按右端点排序,然后把是包含关系的区间预处理一下,比如区间[2, 3]
和[1, 4]
处理为[2,3]
,这是显然的,因为区间有包含关系的时候,小区间如果选了2个数字后,大区间就不用再选了
然后贪心的策略是,如果这个区间需要选新的数字,就尽量选右端点,这样的话可以和后面的区间尽量有重叠
代码
class Solution {
public:
int intersectionSizeTwo(vector<vector<int>>& intervals) {
auto cmp = [](const vector<int> &lhs, const vector<int> &rhs) {
if (lhs[1] == rhs[1]) {
return lhs[0] < rhs[0];
}
return lhs[1] < rhs[1];
};
sort(intervals.begin(), intervals.end(), cmp);
vector<vector<int>> new_intervals;
int left = intervals[0][0];
int right = intervals[0][1];
for (size_t i = 1; i < intervals.size(); ++i) {
if (intervals[i][1] == right) {
left = intervals[i][0];
} else if (intervals[i][0] > left) {
new_intervals.emplace_back(vector<int> {left, right});
left = intervals[i][0];
right = intervals[i][1];
}
}
new_intervals.emplace_back(vector<int> {left, right});
/*for (vector<int> vec : new_intervals) {
cout << vec[0] << " " << vec[1] << endl;
}*/
int first = new_intervals[0][1] - 1;
int second = first + 1;
int ret = 0;
for (size_t i = 1; i < new_intervals.size(); ++i) {
if (new_intervals[i][0] <= second) {
if (new_intervals[i][0] <= first) {
continue;
} else {
first = second;
second = new_intervals[i][1];
++ret;
}
} else {
ret += 2;
first = new_intervals[i][1] - 1;
second = first + 1;
}
}
ret += 2;
return ret;
}
};
复制代码
leetcode周赛题解每周更新,请关注微信公众号 [leetcode周赛题解] 或 [lc_tijie]