Leetcode Weekly Contest 65题解

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]。现在滴完水后,水会按照以下规则流动:

  1. 如果水流能向左流,并且向左流到最低点后,高度比height[K]小,那么向左流
  2. 如果水流不能向左流,则尝试向右流,同样的,向右流到最低点后,高度要比height[K]
  3. 如果水流不能向左向右流,那就停在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

题意

有一个类似于杨辉三角的金字塔三角形,给出了底部,还有一些规则。规则的格式是ABCC的左儿子允许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]

转载于:https://juejin.im/post/5a48c7cf51882511793e89e3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值